Top

appJar module

# -*- coding: utf-8 -*-
#from appJar.appjar import gui
from .appjar import gui

__all__ = ['gui']

Classes

class gui

Class to represent the GUI - Create one of these - add some widgets - call the go() function

class gui(object):
    """ Class to represent the GUI
        - Create one of these
        - add some widgets
        - call the go() function """

    # ensure only one instance of gui is created
    # set to True in constructor
    # set back to false in stop()
    instantiated = False
    built = False

    # static variables
    exe_file = None
    exe_path = None
    lib_file = None
    lib_path = None

    # globals for supported platforms
    WINDOWS = 1
    MAC = 2
    LINUX = 3

    # positioning
    N = N
    NE = NE
    E = E
    SE = SE
    S = S
    SW = SW
    W = W
    NW = NW
    CENTER = CENTER
    LEFT = LEFT
    RIGHT = RIGHT

    # reliefs
    SUNKEN = SUNKEN
    RAISED = RAISED
    GROOVE = GROOVE
    RIDGE = RIDGE
    FLAT = FLAT

    ###################################
    # Constants for music stuff
    ###################################
    BASIC_NOTES = {
        "A": 440,
        "B": 493,
        "C": 261,
        "D": 293,
        "E": 329,
        "F": 349,
        "G": 392,
    }

    NOTES = {'f8': 5587, 'c#6': 1108, 'f4': 349, 'c7': 2093,
             'd#2': 77, 'g8': 6271, 'd4': 293, 'd7': 2349,
            'd#7': 2489, 'g#4': 415, 'e7': 2637, 'd9': 9397,
            'b8': 7902, 'a#4': 466, 'b5': 987, 'b2': 123,
            'g#9': 13289, 'g9': 12543, 'f#2': 92, 'c4': 261,
            'e1': 41, 'e6': 1318, 'a#8': 7458, 'c5': 523,
            'd6': 1174, 'd3': 146, 'g7': 3135, 'd2': 73,
            'd#3': 155, 'g#6': 1661, 'd#4': 311, 'a3': 219,
            'g2': 97, 'c#5': 554, 'd#9': 9956, 'a8': 7040,
            'a#5': 932, 'd#5': 622, 'a1': 54, 'g#8': 6644,
            'a2': 109, 'g#5': 830, 'f3': 174, 'a6': 1760,
            'e8': 5274, 'c#9': 8869, 'f5': 698, 'b1': 61,
            'c#4': 277, 'f#9': 11839, 'e5': 659, 'f9': 11175,
            'f#5': 739, 'a#1': 58, 'f#8': 5919, 'b7': 3951,
            'c#8': 4434, 'g1': 48, 'c#3': 138, 'f#7': 2959,
            'c6': 1046, 'c#2': 69, 'c#7': 2217, 'c3': 130,
            'e9': 10548, 'c9': 8372, 'a#6': 1864, 'a#7': 3729,
            'g#2': 103, 'f6': 1396, 'b3': 246, 'g#3': 207,
            'b4': 493, 'a7': 3520, 'd#6': 1244, 'd#8': 4978,
            'f2': 87, 'd5': 587, 'f7': 2793, 'f#6': 1479,
            'g6': 1567, 'e3': 164, 'f#3': 184, 'g#1': 51,
            'd8': 4698, 'f#4': 369, 'f1': 43, 'c8': 4186,
            'g4': 391, 'g3': 195, 'a4': 440, 'a#3': 233,
            'd#1': 38, 'e2': 82, 'e4': 329, 'a5': 880,
            'a#2': 116, 'g5': 783, 'g#7': 3322, 'b6': 1975,
            'c2': 65, 'f#1': 46
    }

    DURATIONS = {"BREVE": 2000, "SEMIBREVE": 1000, "MINIM": 500,
                "CROTCHET": 250, "QUAVER": 125, "SEMIQUAVER": 63,
                "DEMISEMIQUAVER": 32, "HEMIDEMISEMIQUAVER": 16
    }

###############################################
# USEFUL STATIC METHODS
###############################################

    @staticmethod
    def CENTER(win, up=0):
        gui.SET_LOCATION("CENTER", win=win, up=up)

    @staticmethod
    def SET_LOCATION(x, y=None, ignoreSettings=None, win=None, up=0):
        if ignoreSettings is not None:
            win.ignoreSettings = ignoreSettings

        if gui.GET_PLATFORM() != gui.LINUX:
            trans = win.attributes('-alpha')
            win.attributes('-alpha', 0.0)

        win.update_idletasks()

        if isinstance(x, UNIVERSAL_STRING) and x.lower() in ['c', 'center', 'centre'] and y is None:
            x = y = 'c'
        else:
            x, y = gui.PARSE_TWO_PARAMS(x, y)
        gui.trace("Set location called with %s, %s", x, y)

        # get the window's dimensions
        dims = gui.GET_DIMS(win)

        # set any center positions
        if isinstance(x, UNIVERSAL_STRING) and x.lower() in ['c', 'center', 'centre']: x = dims["x"]
        if isinstance(y, UNIVERSAL_STRING) and y.lower() in ['c', 'center', 'centre']: y = dims["y"]

        # move the window up a bit if requested
        y = y - up if up < y else 0

        # fix any out of bounds positions
        if x < 0 or x > dims['s_width']: x = dims['x']
        if y < 0 or y > dims['s_height']: y = dims['y']

        gui.trace("Screen: %sx%s. Requested: %sx%s. Location: %s, %s",
                    dims["s_width"], dims["s_height"], dims["b_width"],
                    dims["b_height"], x, y)
        win.geometry("+%d+%d" % (x, y))
        win.locationSet = True

        if gui.GET_PLATFORM() != gui.LINUX:
            win.attributes('-alpha', trans)


    @staticmethod
    def CLEAN_CONFIG_DICTIONARY(**kw):
        """ Used by all Classes to tidy up dictionaries passed into config functions
            Allows us to more quickly process the dictionaries when overriding config """

        try: kw['bg'] = kw.pop('background')
        except: pass
        try: kw['fg'] = kw.pop('foreground')
        except: pass
        kw = dict((k.lower().strip(), v) for k, v in kw.items())
        return kw

    @staticmethod
    def GET_PLATFORM():
        """ returns one of the gui class's three static platform variables """
        if platform() in ["win32", "Windows"]:
            return gui.WINDOWS
        elif platform() == "Darwin":
            return gui.MAC
        elif platform() in ["Linux", "FreeBSD"]:
            return gui.LINUX
        else:
            raise Exception("Unknown platform: " + platform())

    @staticmethod
    def SHOW_VERSION():
        """ returns a printable string containing version information """
        verString = \
            "appJar: " + str(__version__) \
            + "\nPython: " + str(sys.version_info[0]) \
            + "." + str(sys.version_info[1]) + "." + str(sys.version_info[2]) \
            + "\nTCL: " + str(TclVersion) \
            + ", TK: " + str(TkVersion) \
            + "\nPlatform: " + str(platform()) \
            + "\npid: " + str(os.getpid()) \
            + "\nlocale: " + str(__locale__)

        return verString

    @staticmethod
    def SHOW_PATHS():
        """ returns a printable string containing path to libraries, etc """
        pathString = \
            "File Name: " + (gui.exe_file if gui.exe_file is not None else "") \
            + "\nFile Location: " + (gui.exe_path if gui.exe_path is not None else "") \
            + "\nLib Location: " + (gui.lib_path if gui.lib_path is not None else "")

        return pathString

    @staticmethod
    def GET_DIMS(container):
        """ returns a dictionary of dimensions for the supplied container """
        container.update()
        dims = {}
        # get the apps requested width & height
        dims["r_width"] = container.winfo_reqwidth()
        dims["r_height"] = container.winfo_reqheight()

        # get the current width & height
        dims["w_width"] = container.winfo_width()
        dims["w_height"] = container.winfo_height()

        # get the window's width & height
        dims["s_width"] = container.winfo_screenwidth()
        dims["s_height"] = container.winfo_screenheight()

        # determine best geom for OS
        # on MAC & LINUX, w_width/w_height always 1 unless user-set
        # on WIN, w_height is bigger then r_height - leaving empty space
        if gui.GET_PLATFORM() in [gui.MAC, gui.LINUX]:
            if dims["w_width"] != 1:
                dims["b_width"] = dims["w_width"]
                dims["b_height"] = dims["w_height"]
            else:
                dims["b_width"] = dims["r_width"]
                dims["b_height"] = dims["r_height"]
        else:
            dims["b_height"] = max(dims["r_height"], dims["w_height"])
            dims["b_width"] = max(dims["r_width"], dims["w_width"])

        # GUI's corner - widget's corner
        # widget's corner can be 0 on windows when size not set by user
        dims["outerFrameWidth"] = 0 if container.winfo_x() == 0 else container.winfo_rootx() - container.winfo_x()
        dims["titleBarHeight"] = 0 if container.winfo_rooty() == 0 else container.winfo_rooty() - container.winfo_y()

        # add it all together
        dims["actualWidth"] = dims["b_width"] + (dims["outerFrameWidth"] * 2)
        dims["actualHeight"] = dims["b_height"] + dims["titleBarHeight"] + dims["outerFrameWidth"]

        dims["x"] = (dims["s_width"] // 2) - (dims["actualWidth"] // 2)
        dims["y"] = (dims["s_height"] // 2) - (dims["actualHeight"] // 2)

        return dims

    @staticmethod
    def PARSE_TWO_PARAMS(x, y):
        """ used to convert different possible x/y params to a tuple
        """
        if y is not None:
            return (x,y)
        else:
            if isinstance(x, (list, tuple)):
                return (x[0], x[1])
            else:
                if isinstance(x, UNIVERSAL_STRING):
                    x=x.strip()
                    if "," in x:
                        return [int(w.strip()) for w in x.split(",")]
                return (x, x)

    @staticmethod
    def SPLIT_GEOM(geom):
        """ returns 2 lists made from the geom string
        :param geom: the geom string to parse
        :returns: a tuple containing a width/heiht tuple & a x/y position tuple
        """
        geom = geom.lower().split("x")
        width = int(float(geom[0]))
        height = int(float(geom[1].split("+")[0]))
        try:
            x = int(float(geom[1].split("+")[1]))
            y = int(float(geom[1].split("+")[2]))
        except IndexError:
            x = y = -1

        return (width, height), (x, y)

    @staticmethod
    def MOUSE_POS_IN_WIDGET(widget, event, findRoot=True):
        """ returns the mouse's relative position in a widget
        :param widget: the widget to look in
        :param event: the event containing the mouse coordinates
        :param findRoot: if we should make this relative to the parent
        """

        # first we have to get the real master
        master = widget
        while findRoot:
            if isinstance(master, (SubWindow, Tk)):
                findRoot = False
            else:
                master = master.master

        # subtract the widget's top left corner from the root window's top corner
        x = event.x_root - master.winfo_rootx()
        y = event.y_root - master.winfo_rooty()
        gui.trace("<<MOUSE_POS_IN_WIDGET>> %s %s,%s", widget, x, y)
        return (x, y)

#####################################
#####################################
# CONSTRUCTOR - creates the GUI
#####################################
    def __init__(
                    self, title=None, geom=None, handleArgs=True, language=None,
                    startWindow=None, useTtk=False, useSettings=False, showIcon=True, **kwargs
                ):
        """ constructor - sets up the empty GUI window, and inits the various properties """

        if self.__class__.instantiated:
            raise Exception("You cannot have more than one instance of gui, try using a subWindow.")
        else:
            self.__class__.instantiated = True

        self.alive = True

        # first up, set the logger
        def _logForLevel(self, message, *args, **kwargs):
            if self.isEnabledFor(logging.DEBUG-5):
                self._log(logging.DEBUG-5, message, args, **kwargs)
        def _logToRoot(message, *args, **kwargs):
            logging.log(logging.DEBUG-5, message, *args, **kwargs)

        logging.basicConfig(level=logging.WARNING, format='%(asctime)s %(name)s:%(levelname)s %(message)s')
        logging.addLevelName(logging.DEBUG - 5, 'TRACE')
        setattr(logging, 'TRACE', logging.DEBUG -5)
        setattr(logging.getLoggerClass(), "trace", _logForLevel)
        setattr(logging, "trace", _logToRoot)

        logFile = kwargs.pop("file", kwargs.pop("logFile", None))
        logLevel = kwargs.pop("log", kwargs.pop("logLevel", None))

        self._language = language
        self.useSettings = useSettings
        self.settingsFile = "appJar.ini"
        self.externalSettings = {}

        self.startWindow = startWindow

        # check any command line arguments
        if argparse is None: handleArgs = False
        args = self._handleArgs() if handleArgs else None

        # warn if we're in an untested mode
        self._checkMode()
        # first out, verify the platform
        self.platform = gui.GET_PLATFORM()

        # process any command line arguments
        self.ttkFlag = False
        selectedTtkTheme = None
        if handleArgs:
            if args.f:
                gui.setLogFile(args.f)
                logFile = None # don't use any param logFile

            tmplevel, logLevel = logLevel, None
            if args.c: gui.setLogLevel("CRITICAL")
            elif args.e: gui.setLogLevel("ERROR")
            elif args.w: gui.setLogLevel("WARNING")
            elif args.i: gui.setLogLevel("INFO")
            elif args.d: gui.setLogLevel("DEBUG")
            elif args.t: gui.setLogLevel("TRACE")
            else: loglevel = tmplevel

        if logFile is not None: gui.setLogFile(logFile)
        if logLevel is not None: gui.setLogLevel(logLevel)

        if handleArgs:
            if args.l: self._language = args.l
            if args.ttk:
                useTtk = True
                if args.ttk is not True:
                    selectedTtkTheme = args.ttk

            if args.s:
                self.useSettings = True
                if args.s is not True:
                    self.settingsFile = args.s

        # configure as ttk
        if useTtk:
            self._useTtk()
            if useTtk is not True:
                selectedTtkTheme = useTtk

        # a stack to hold containers as being built
        # done here, as initArrays is called elsewhere - to reset the gubbins
        self.containerStack = []
        self.translations = {"POPUP":{}, "SOUND":{}, "EXTERNAL":{}}
        # first up, set up all the data stores
        self.widgetManager = WidgetManager()
        self.accessMade = False # accessibility subWindow
        self.splashConfig = None # splash screen?
        self.dnd = None # the dnd manager
        self.doFlash = False # set up flash variable
        self.hasTitleBar = True # used to hide/show title bar

        # validate function callbacks - used by numeric texts
        # created first time a widget is used
        self.validateNumeric = None
        self.validateSpinBox = None

        # dynamically create lots of functions for configuring stuff
        self._buildConfigFuncs()
        # language parser
        self.configParser = None

        # set up some default path locations
        # this fails if in interactive mode....
        try:
            gui.exe_file = str(os.path.basename(theMain.__file__))
            gui.exe_path = str(os.path.dirname(theMain.__file__))
        except:
            pass

        gui.lib_file = os.path.abspath(__file__)
        gui.lib_path = os.path.dirname(gui.lib_file)

        # location of appJar
        self.resource_path = os.path.join(gui.lib_path, "resources")
        self.icon_path = os.path.join(self.resource_path, "icons")
        self.sound_path = os.path.join(self.resource_path, "sounds")
        self.appJarIcon = os.path.join(self.icon_path, "favicon.ico")

        # user configurable
        self.userImages = gui.exe_path
        self.userSounds = gui.exe_path

        # create the main window - topLevel
        self.topLevel = Tk()
        self.topLevel.bind('<Configure>', self._windowEvent)

        def _setFocus(e):
            try: e.widget.focus_set()
            except: pass

        # these are specifically to make right-click menus disapear on linux
        self.topLevel.bind('<Button-1>', lambda e: _setFocus(e))
        self.topLevel.bind('<Button-2>', lambda e: _setFocus(e))
        self.topLevel.bind('<Button-3>', lambda e: _setFocus(e))
        # override close button
        self.topLevel.protocol("WM_DELETE_WINDOW", self.stop)
        # temporarily hide it
        self.topLevel.withdraw()

        # used to keep a handle on the last pop-up dialog
        # allows the dialog to be closed remotely
        # mainly for test-automation
        self.topLevel.POP_UP = None

        # create a frame to store all the widgets
        # now a canvas to allow animation...
        self.appWindow = CanvasDnd(self.topLevel)
        self.appWindow.pack(fill=BOTH, expand=True)
        self.topLevel.canvasPane = self.appWindow

        # set the windows title
        if title is None:
            title = "appJar" if gui.exe_file is None else gui.exe_file

        self.setTitle(title)
        self.topLevel.winIcon = None # will store the path to any icon

        # configure the geometry of the window
        self.topLevel.escapeBindId = None  # used to exit fullscreen
        self.topLevel.stopFunction = None  # used to exit fullscreen
        self.topLevel.startFunction = None

        # set the resize status - default to True
        self.topLevel.locationSet = False
        self.topLevel.ignoreSettings = False
        self.topLevel.isFullscreen = False # records if we're in fullscreen - stops hideTitle from breaking
        self.topLevel.displayed = True
        if geom is not None: self.setSize(geom)
        self.setResizable(True)

        self.Widgets = WIDGET_NAMES

        # 3 fonts used for most widgets
        self._buttonFont = tkFont.Font(family="Helvetica", size=12,)
        self._labelFont = tkFont.Font(family="Helvetica", size=12)
        self._inputFont = tkFont.Font(family="Helvetica", size=12)
        self._statusFont = tkFont.Font(family="Helvetica", size=12)

        # dedicated font for access widget
        self._accessFont = tkFont.Font(family="Arial", size=11,)
        # dedicated font for links - forces bold & underlined, but updated with label fonts
        self._linkFont = tkFont.Font(family="Helvetica", size=12, weight='bold', underline=1)

        self.tableFont = tkFont.Font(family="Helvetica", size=12)

        # create a menu bar - only shows if populated
        # now created in menu functions, as it generated a blank line...
        self.hasMenu = False
        self.hasStatus = False
        self.copyAndPaste = CopyAndPaste(self.topLevel, self)

        class Toolbar(frameBase, object):
            def __init__(self, master, **kwargs):
                super(Toolbar, self).__init__(master, **kwargs)
                self.BG_COLOR = None
                self.pinned = True
                self.pinBut = None
                self.inUse = False
                self.toolbarMin = None
                self.location = None

            def makeMinBar(self):
                if self.toolbarMin is None:
                    self.toolbarMin = Frame(self.master, bd=1, relief=RAISED)
                    self.toolbarMin.config(bg="gray", height=3)
                    self.bind("<Leave>", self._minToolbar)
                    self.toolbarMin.bind("<Enter>", self._maxToolbar)

            def hide(self):
                if self.inUse:
                    self.pack_forget()
                    if self.toolbarMin is not None:
                        self.toolbarMin.pack_forget()

            def show(self):
                if self.inUse:
                    self.pack(before=self.location, side=TOP, fill=X)
                    if self.toolbarMin is not None:
                        self.toolbarMin.pack_forget()

            def _minToolbar(self, e=None):
                if not self.pinned:
                    if self.toolbarMin is not None:
                        self.toolbarMin.config(width=self.winfo_reqwidth())
                        self.toolbarMin.pack(before=self.location, side=TOP, fill=X)
                    self.pack_forget()

            def _maxToolbar(self, e=None):
                self.pack(before=self.location, side=TOP, fill=X)
                if self.toolbarMin is not None:
                    self.toolbarMin.pack_forget()

        class WidgetContainer(frameBase, object):
            def __init__(self, master, **kwargs):
                super(WidgetContainer, self).__init__(master, **kwargs)

        # create the main container for this GUI
        container = WidgetContainer(self.appWindow)
        # container = Label(self.appWindow) # made as a label, so we can set an
        # image
        if not self.ttkFlag:
            container.config(padx=2, pady=2, background=self.topLevel.cget("bg"))
        container.pack(fill=BOTH, expand=True)
        self._addContainer("root", WIDGET_NAMES.RootPage, container, 0, 1)

        self.tb = Toolbar(self.appWindow)
        if not self.ttkFlag:
            self.tb.config(bd=1, relief=RAISED)
        else:
            self.tb.config(style="Toolbar.TFrame")


        # set up the main container to be able to host an image
        self._configBg(container)

        if self.platform == self.WINDOWS and showIcon:
            try:
                self.setIcon(self.appJarIcon)
            except: # file not found
                gui.trace("Error setting Windows default icon")

        # set the ttk theme
        if self.ttkFlag:
            self.setTtkTheme(selectedTtkTheme)

        # for configuting event processing
        self.EVENT_SIZE = 1000
        self.EVENT_SPEED = 100
        self.preloadAnimatedImageId = None
        self.processQueueId = None

        # an array to hold any threaded events....
        self.events = []
        self.pollTime = 250
        self._fastStop = False
        self.configure(**kwargs)

        # special bindings
        self._globalBindings()

        self.built = True

    def _globalBindings(self):
        def _selectEntry(event):
            event.widget.select_range(0, 'end')

        def _selectText(event):
            event.widget.tag_add("sel","1.0","end")

        def _scrollPaste(event):
            event.widget.event_generate('<<Paste>>')
            event.widget.see(END)

        if self.GET_PLATFORM() == self.MAC:
            self.topLevel.bind_class("Text", "<Command-a>", _selectText)
            self.topLevel.bind_class("Entry", "<Command-a>", _selectEntry)
            self.topLevel.bind_class("Text", "<Command-v>", _scrollPaste)
        else:
            self.topLevel.bind_class("Text", "<Control-a>", _selectText)
            self.topLevel.bind_class("Entry", "<Control-a>", _selectEntry)
            self.topLevel.bind_class("Text", "<Control-v>", _scrollPaste)

    def _handleArgs(self):
        """ internal function to handle command line arguments """
        parser = argparse.ArgumentParser(
            description="appJar - the easiest way to create GUIs in python",
            epilog="For more information, go to: http://appJar.info"
        )
        parser.add_argument("-v", "--version", action="version", version=gui.SHOW_VERSION(), help="show version information and exit")
        logGroup = parser.add_mutually_exclusive_group()
        logGroup.add_argument("-c", action="store_const", const=True, help="only log CRITICAL messages")
        logGroup.add_argument("-e", action="store_const", const=True, help="log ERROR messages and above")
        logGroup.add_argument("-w", action="store_const", const=True, help="log WARNING messages and above")
        logGroup.add_argument("-i", action="store_const", const=True, help="log INFO messages and above")
        logGroup.add_argument("-d", action="store_const", const=True, help="log DEBUG messages and above")
        logGroup.add_argument("-t", action="store_const", const=True, help="log TRACE messages and above")
        parser.add_argument("-l", metavar="LANGUAGE.ini", help="set a language file to use")
        parser.add_argument("-f", metavar="file.log", help="set a log file to use")
        parser.add_argument("-s", metavar="SETTINGS", const=True, nargs="?", help="load settings, from an optional file name")
        parser.add_argument("--ttk", metavar="THEME", const=True, nargs="?", help="enable ttk, with an optional theme")
        return parser.parse_args()

    # function to check on mode
    def _checkMode(self):
        """ internal function to warn about issues in certain modes """
        # detect if we're in interactive mode
        if hasattr(sys, 'ps1'):
            self.warn("Interactive mode is not fully tested, some features might not work.")
        else:
            if sys.flags.interactive:
                self.warn("Postmortem Interactive mode is not fully tested, some features might not work.")
        # also, check for iPython
        try:
            __IPYTHON__
        except NameError:
            #no iPython - ignore
            pass
        else:
            self.warn("iPython is not fully tested, some features might not work.")

    def _configBg(self, container):
        """ internal function to set up a label as the BG """
        # set up a background image holder
        # alternative to label option above, as label doesn't update widgets
        # properly

        class BgLabel(labelBase, object):
            def __init__(self, master, **kwargs):
                super(BgLabel, self).__init__(master, **kwargs)

        if not self.ttkFlag:
            self.bgLabel = BgLabel(container, anchor=CENTER, font=self._getContainerProperty('labelFont'), background=self._getContainerBg())
        else:
            self.bgLabel = ttk.Label(container)
        self.bgLabel.place(x=0, y=0, relwidth=1, relheight=1)
        container.image = None

#####################################
# TTK functions
#####################################

    def _useTtk(self):
        """ enables use of ttk """
        global ttk, frameBase, labelBase, scaleBase, entryBase
        try:
            import ttk
        except:
            try:
                from tkinter import ttk
            except:
                gui.error("ttk not available")
                return
        self.ttkFlag = True
        frameBase = ttk.Frame
        labelBase = ttk.Label
        scaleBase = ttk.Scale
        entryBase = ttk.Entry

        gui.trace("Mode switched to ttk")

    def _loadTtkThemes(self):
        global ThemedStyle
        if ThemedStyle is None:
            try:
                from ttkthemes import ThemedStyle
                self.ttkStyle = ThemedStyle(self.topLevel)
            except:
                ThemedStyle = False

    def getTtkThemes(self, loadThemes=False):
        if loadThemes:
            self._loadTtkThemes()
            if not ThemedStyle:
                self.error("Custom ttkThemes not available")

        return self.ttkStyle.theme_names()

    def getTtkTheme(self):
        return self.ttkStyle.theme_use()

    # only call this after the main tk has been created
    # otherwise we get two windows!
    def setTtkTheme(self, theme=None):
        """ sets the ttk theme to use """
        self.ttkStyle = ttk.Style()

        gui.trace("Switching ttk theme to: %s", theme)
        if theme is not None:
            try:
                self.ttkStyle.theme_use(theme)
            except:
                gui.trace("no basic ttk theme named %s found, searching for additional themes", theme)
                self._loadTtkThemes()
                if not ThemedStyle:
                    self.error("ttk theme: %s unavailable. Try one of: %s", theme, str(self.ttkStyle.theme_names()))
                else:
                    self.ttkStyle.set_theme(theme)

        # set up our ttk styles
        self.ttkStyle.configure("DefaultText.TEntry", foreground="grey")
        self.ttkStyle.configure("ValidationEntryValid.TEntry", foreground="#4CC417", highlightbackground="#4CC417", highlightcolor="#4CC417", highlightthickness='20')
        self.ttkStyle.configure("ValidationEntryInvalid.TEntry", foreground="#FF0000", highlightbackground="#FF0000", highlightcolor="#FF0000", highlightthickness='20')
        self.ttkStyle.configure("ValidationEntryWait.TEntry", foreground="#000000", highlightbackground="#000000", highlightcolor="#000000", highlightthickness='20')

        self.ttkStyle.configure("ValidationEntryValid.TLabel", foreground="#4CC417")
        self.ttkStyle.configure("ValidationEntryInvalid.TLabel", foreground="#FF0000")
        self.ttkStyle.configure("ValidationEntryWait.TLabel", foreground="#000000")

        self.ttkStyle.configure("Link.TLabel", foreground="#0000ff")
        self.ttkStyle.configure("LinkOver.TLabel", foreground="#3366ff")

        #toolbars
        self.ttkStyle.configure("Toolbar.TFrame")
        self.ttkStyle.configure("Toolbar.TLabel")
        self.ttkStyle.configure("Toolbar.TButton", compound=CENTER, padding=0, expand=0)

#        self.fgColour = self.topLevel.cget("foreground")
#        self.buttonFgColour = self.topLevel.cget("foreground")
#        self.labelFgColour = self.topLevel.cget("foreground")

    # set a property for ttk theme
    ttkTheme = property(getTtkTheme, setTtkTheme)

###############################################################
# library loaders - on demand loading of different classes
###############################################################

    def _loadRandom(self):
        """ loasd random libraries """
        global random
        if random is None:
            import random

    def _loadTurtle(self):
        """ loasd turtle libraries """
        global turtle
        try:
            import turtle
        except:
            turtle = False
            self.error("Turtle not available")

    def _loadConfigParser(self):
        """ loads the ConfigParser, used by internationalisation & settings """
        global ConfigParser, ParsingError, codecs
        if ConfigParser is None:
            try:
                from configparser import ConfigParser
                from configparser import ParsingError
                import codecs
            except:
                try:
                    from ConfigParser import ConfigParser
                    from ConfigParser import ParsingError
                    import codecs
                except:
                    ConfigParser = ParsingError = codecs = False
                    self.configParser = None
                    return
            self.configParser = ConfigParser()
            self.configParser.optionxform = str

    def _loadHashlib(self):
        """ loads hashlib - used by text area """
        global hashlib
        if hashlib is None:
            try:
                import hashlib
            except:
                hashlib = False

    def _loadTooltip(self):
        """ loads tooltips - used all over """
        global ToolTip
        if ToolTip is None:
            try:
                from appJar.lib.tooltip import ToolTip
            except:
                ToolTip = False

    def _loadMatplotlib(self):
        """ loads matPlotLib """
        global PlotCanvas, PlotNav, PlotFig

        if PlotCanvas is None:
            try:
                from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg as PlotCanvas
                try: from matplotlib.backends.backend_tkagg import NavigationToolbar2Tk as PlotNav
                except: from matplotlib.backends.backend_tkagg import NavigationToolbar2TkAgg as PlotNav
                from matplotlib.figure import Figure as PlotFig
            except:
                PlotCanvas = PlotNav = PlotFig = False

    def _loadExternalDnd(self):
        """ loads external dnd - from other applications """
        global EXTERNAL_DND
        if EXTERNAL_DND is None:
            try:
                tkdndlib = os.path.join(os.path.dirname(os.path.abspath(__file__)), "lib", "tkdnd2.8")
                os.environ['TKDND_LIBRARY'] = tkdndlib
                from appJar.lib.TkDND_wrapper import TkDND as EXTERNAL_DND
                self.dnd = EXTERNAL_DND(self.topLevel)
            except:
                EXTERNAL_DND = False

    def _loadInternalDnd(self):
        """ loads the internal dnd libraries """
        global INTERNAL_DND, types
        if INTERNAL_DND is None:
            try:
                import Tkdnd as INTERNAL_DND
                import types as types
            except:
                try:
                    from tkinter import dnd as INTERNAL_DND
                    import types as types
                except:
                    INTERNAL_DND = False
                    types = False

    def _loadURL(self):
        """ loads ibraries used by googlemaps widget """
        global base64, urlencode, urlopen, urlretrieve, quote_plus, json, Queue
        self._loadThreading()
        if Queue:
            if urlencode is None:
                try: # python 2
                    from urllib import urlencode, urlopen, urlretrieve, quote_plus
                    import json
                    import base64
                except ImportError: # python 3
                    try:
                        from urllib.parse import urlencode
                        from urllib.parse import quote_plus
                        from urllib.request import urlopen
                        from urllib.request import urlretrieve
                        import json
                        import base64
                    except:
                        base64 = urlencode = urlopen = urlretrieve = quote_plus = json = Queue = False
        else:
            base64 = urlencode = urlopen = urlretrieve = quote_plus = json = Queue = False

    def _loadThreading(self):
        """ loads threading classes, and sets up queue """
        global Thread, Queue
        if Thread is None:
            try:
                from threading import Thread
                import Queue
            except ImportError: # python 3
                try:
                    from threading import Thread
                    import queue as Queue
                except:
                    Thread = Queue = False
                    return

            self.eventQueue = Queue.Queue(maxsize=self.EVENT_SIZE)
            self._processEventQueue()

    def _loadNanojpeg(self):
        """ loads jpeg support """
        global nanojpeg, array
        if nanojpeg is None:
            try:
                from appJar.lib import nanojpeg
                import array
            except:
                nanojpeg = False
                array = False

    def _loadWinsound(self):
        """ loads winsound support on Windows """
        global winsound
        if winsound is None:
            if platform() in ["win32", "Windows"]:
                import winsound
            else:
                winsound = False

    def _importPngimagetk(self):
        """ loads PNG support """
        global PngImageTk
        if PngImageTk is None:
            try:
                from appJar.lib.tkinter_png import PngImageTk
            except:
                PngImageTk = False

    def _importAjtree(self):
        """ loads tree support - and creates tree classes """
        global parseString, TreeItem, TreeNode

        if TreeNode is None:
            try:
                from idlelib.TreeWidget import TreeItem, TreeNode
            except:
                try:
                    from idlelib.tree import TreeItem, TreeNode
                except:
                    gui.warning("no trees")
                    TreeItem = TreeNode = parseString = False

            if TreeNode is not False:
                try:
                    from xml.dom.minidom import parseString
                except:
                    gui.warning("no parse string")
                    TreeItem = TreeNode = parseString = False
                    return

    def _importSqlite3(self):
        """ loads sqlite3 """
        global sqlite3
        if sqlite3 is None:
            try:
                import sqlite3
            except:
                sqlite3 = False

    def _importWebBrowser(self):
        """ loads webbrowser """
        global webbrowser
        if webbrowser is None:
            try:
                import webbrowser
            except:
                webbrowser = False

#####################################
# FUNCTIONS FOR UNIVERSAL DND
#####################################

    def _registerExternalDragSource(self, title, widget, function=None):
        """ register a widget to start external drag events """
        self._loadExternalDnd()

        if EXTERNAL_DND is not False:
            try:
                self.dnd.bindsource(widget, self._startExternalDrag, 'text/uri-list')
                self.dnd.bindsource(widget, self._startExternalDrag, 'text/plain')
                widget.dndFunction = function
                widget.dragData = None
            except:
                # dnd not working on this platform
                raise Exception("Failed to register external Drag'n Drop for: " + str(title))
        else:
            raise Exception("External Drag'n Drop not available on this platform")

    def _registerExternalDropTarget(self, title, widget, function=None, replace=True):
        """ register a widget to receive external drag events """
        self._loadExternalDnd()

        if EXTERNAL_DND is not False:
            try:
                self.dnd.bindtarget(widget, self._receiveExternalDrop, 'text/uri-list')
                self.dnd.bindtarget(widget, self._receiveExternalDrop, 'text/plain')
                # cater for new drop parameter in new setters
                if function == True: function = None
                widget.dndFunction = function
                widget.dropData = None
                widget.dropReplace = replace
            except:
                # dnd not working on this platform
                raise Exception("Failed to register external Drag'n Drop for: " + str(title))
        else:
            raise Exception("External Drag'n Drop not available on this platform")

    def _registerInternalDragSource(self, kind, title, widget, function=None):
        """ register a widget to start internal drag events """
        self._loadInternalDnd()

        name = None
        if kind == WIDGET_NAMES.Label:
            name = self.getLabel(title)

        if INTERNAL_DND is not False:
            try:
                widget.bind('<ButtonPress>', lambda e: self._startInternalDrag(e, title, name, widget))
                widget.dnd_canvas = self._getCanvas().canvasPane
                gui.trace("DND drag source created: %s on canvas %s", widget, widget.dnd_canvas)
            except:
                raise Exception("Failed to register internal Drag'n Drop for: " + str(title))
        else:
            raise Exception("Internal Drag'n Drop not available on this platform")

    def _registerInternalDropTarget(self, widget, function):
        """ register a widget to receive internal drag events """
        gui.trace("<<WIDGET._registerInternalDropTarget>> %s", widget)
        self._loadInternalDnd()
        if not INTERNAL_DND:
            raise Exception("Internal Drag'n Drop not available on this platform")

        # called by DND class, when looking for a DND target
        def dnd_accept(self, source, event):
            gui.trace("<<WIDGET.dnd_accept>> %s - %s", widget, self.dnd_canvas)
            return self

        # This is called when the mouse pointer goes from outside the
        # Target Widget to inside the Target Widget.
        def dnd_enter(self, source, event):
            gui.trace("<<WIDGET.dnd_enter>> %s", widget)
            XY = gui.MOUSE_POS_IN_WIDGET(self,event)
            source.appear(self, XY)

        # This is called when the mouse pointer goes from inside the
        # Target Widget to outside the Target Widget.
        def dnd_leave(self, source, event):
            gui.trace("<<WIDGET.dnd_leave>> %s", widget)
            # hide the dragged object
            source.vanish()

        #This is called if the DraggableWidget is being dropped on us.
        def dnd_commit(self, source, event):
            source.vanish(all=True)
            gui.trace("<<WIDGET.dnd_commit>> %s Object received=%s", widget, source)

        #This is called when the mouse pointer moves within the TargetWidget.
        def dnd_motion(self, source, event):
            gui.trace("<<WIDGET.dnd_motion>> %s", widget)
            XY = gui.MOUSE_POS_IN_WIDGET(self,event)
            # move the dragged object
            source.move(self, XY)

        def keepWidget(self, title, name):
            if self.drop_function is not None:
                return self.drop_function(title, name)
            else:
                self.configParser(text=name)
                return True

        widget.dnd_accept = types.MethodType(dnd_accept, widget)
        widget.dnd_enter = types.MethodType(dnd_enter, widget)
        widget.dnd_leave = types.MethodType(dnd_leave, widget)
        widget.dnd_commit = types.MethodType(dnd_commit, widget)
        widget.dnd_motion = types.MethodType(dnd_motion, widget)
        widget.keepWidget = types.MethodType(keepWidget, widget)
        # save the underlying canvas
        widget.dnd_canvas = self._getCanvas().canvasPane
        widget.drop_function = function

        gui.trace("DND target created: %s on canvas %s", widget, widget.dnd_canvas)

    def _startInternalDrag(self, event, title, name, widget):
        """ called when the user initiates an internal drag event """
        gui.trace("Internal drag started for %s on %s", title, widget)

        x, y = gui.MOUSE_POS_IN_WIDGET(widget, event, False)
        width = x / widget.winfo_width()
        height = y / widget.winfo_height()

        thingToDrag = DraggableWidget(widget.dnd_canvas, title, name, (width, height))
        INTERNAL_DND.dnd_start(thingToDrag, event)

    def _startExternalDrag(self, event):
        """ starts external drags - not yet supported """
        widgType = gui.GET_WIDGET_CLASS(event.widget)
        self.warn("Unable to initiate drag events: %s", widgType)

    def _receiveExternalDrop(self, event):
        """ receives external drag events """
        widgType = gui.GET_WIDGET_CLASS(event.widget)
        event.widget.dropData = event.data
        if not hasattr(event.widget, 'dndFunction'):
            self.warn("Error - external drop target not correctly configured: %s", widgType)
        elif event.widget.dndFunction is not None:
            event.widget.dndFunction(event.data)
        else:
            if widgType in ["Entry", "AutoCompleteEntry", "SelectableLabel"]:
                if widgType == "SelectableLabel": event.widget.configure(state="normal")
                if event.widget.dropReplace:
                    event.widget.delete(0, END)
                event.widget.insert(END, event.data)
                event.widget.focus_set()
                event.widget.icursor(END)
                if widgType == "SelectableLabel": event.widget.configure(state="readonly")
            elif widgType in ["TextArea", "AjText", "ScrolledText", "AjScrolledText"]:
                if event.widget.dropReplace:
                    event.widget.delete(1.0, END)
                event.widget.insert(END, event.data)
                event.widget.focus_set()
                event.widget.see(END)
            elif widgType in ["Label"]:
                for k, v in self.widgetManager.group(WIDGET_NAMES.Image).items():
                    if v == event.widget:
                        try:
                            imgTemp = self.userImages
                            image = self._getImage(event.data, False)
                            self._populateImage(k, image)
                            self.userImages = imgTemp
                        except:
                            self.errorBox("Error loading image", "Unable to load image: " + str(event.data))
                        return
                for k, v in self.widgetManager.group(WIDGET_NAMES.Label).items():
                    if v == event.widget:
                        self.setLabel(k, event.data)
                        return
            elif widgType in ["Listbox"]:
                for k, v in self.widgetManager.group(WIDGET_NAMES.ListBox).items():
                    if v == event.widget:
                        self.addListItem(k, event.data)
                        return
            elif widgType in ["Message"]:
                for k, v in self.widgetManager.group(WIDGET_NAMES.Message).items():
                    if v == event.widget:
                        self.setMessage(k, event.data)
                        return
            else:
                self.warn("Unable to receive drop events: %s", widgType)


#####################################
# Language/Translation functions
#####################################
    def translate(self, key, default=None):
        """ returns a translated version of the key, using the current language
            if none found, returns the default value """
        return self._translate(key, "EXTERNAL", default)

    def _translateSound(self, key):
        """ internal wrapper to translate sounds """
        return self._translate(key, "SOUND", key)

    def _translatePopup(self, key, value):
        """ internal wrapper to translate popups """
        pop = self._translate(key, "POPUP")
        if pop is None:
            return (key, value)
        else:
            return (pop[0], pop[1])

    def _translate(self, key, section, default=None):
        """ returns the relevant key from the relevant section in the internally
            held translation dictionary - prepopulated when language was set """
        if key in self.translations[section]:
            return self.translations[section][key]
        else:
            return default

    def getLanguage(self):
        ''' returns the current language '''
        return self._language

    def setLanguage(self, language):
        """ wrapper for changeLanguage() """
        self.changeLanguage(language)

    # function to update languages
    def changeLanguage(self, language):
        """ changes the language used by the GUI
            will iterate through all widgets and update their text
            as well as populate a translation dictionary for later lookups """
        self._loadConfigParser()
        if not ConfigParser:
            self.error("Internationalisation not supported")
            return

        fileName = language.upper() + ".ini"
        gui.trace("Loading language file: %s", fileName)
        if not PYTHON2:
            try:
                with codecs.open(fileName, "r", "utf8") as langFile:
                    self.configParser.read_file(langFile)
            except FileNotFoundError:
                self.error("Invalid language, file not found: %s", fileName)
                return
        else:
            try:
                try:
                    with codecs.open(fileName, "r", "utf8") as langFile:
                        self.configParser.read_file(langFile)
                except AttributeError:
                    with codecs.open(fileName, "r", "utf8") as langFile:
                        self.configParser.readfp(langFile)
            except IOError:
                self.error("Invalid language, file not found: %s", fileName)
                return
            except ParsingError:
                self.error("Translation failed - language file contains errors, ensure there is no whitespace at the beginning of any lines.")
                return

        gui.trace("Switching to: %s", language)
        self._language = language
        self.translations = {"POPUP":{}, "SOUND":{}, "EXTERNAL":{}}
        # loop through each section, get the relative set of widgets
        # change the text
        for section in self.configParser.sections():
            getWidgets = True
            section = section.upper()
            gui.trace("\tSection: %s", section)

            # convert the section title to its code
            if section == "CONFIG":
                # skip the config section (for now)
                gui.trace("\tSkipping CONFIG")
                continue
            elif section == "TITLE":
                kind = WIDGET_NAMES.SubWindow
            elif section.startswith("TOOLTIP-"):
                kind = "TOOLTIP"
                getWidgets = False
            elif section in ["SOUND", "EXTERNAL", "POPUP"]:
                for (key, val) in self.configParser.items(section):
                    if section == "POPUP": val = val.strip().split("\n")
                    self.translations[section][key] = val
                    gui.trace("\t\t%s: %s", key, val)
                continue
            elif section == "MENUBAR":
                for (key, val) in self.configParser.items(section):
                    key = key.strip().split("-")
                    gui.trace("\t\t%s: %s", key, val)
                    if len(key) == 1:
                        try:
                            self.renameMenu(key[0], val)
                        except:
                            self.warn("Invalid key")
                    elif len(key) == 2:
                        try:
                            self.renameMenuItem(key[0], key[1], val)
                        except:
                            self.warn("Invalid key")
                continue
            else:
                try:
                    kind = WIDGET_NAMES.getIgnoreCase(section)
                except Exception:
                    self.warn("Invalid config section: %s", section)
                    continue

            # if necessary, use the code to get the widget list
            if getWidgets:
                widgets = self.widgetManager.group(kind)

            if kind in [WIDGET_NAMES.Scale]:
                self.warn("No text is displayed in %s. Maybe it has a Label?", section)
                continue
            elif kind in [WIDGET_NAMES.TextArea, WIDGET_NAMES.Meter, WIDGET_NAMES.PieChart, WIDGET_NAMES.Tree]:
                self.warn("No text is displayed in %s", section)
                continue
            elif kind in [WIDGET_NAMES.name(WIDGET_NAMES.SubWindow)]:
                for (key, val) in self.configParser.items(section):
                    gui.trace("\t\t%s: %s", key, val)

                    if key.lower() == "appjar":
                        self.setTitle(val)
                    elif key.lower() == "splash":
                        if self.splashConfig is not None:
                            gui.trace("\t\t Updated SPLASH to: %s", val)
                            self.splashConfig['text'] = val
                        else:
                            gui.trace("\t\t No SPLASH to update")
                    elif key.lower() == "statusbar":
                        gui.trace("\tSetting STATUSBAR: %s", val)
                        self.setStatusbarHeader(val)
                    else:
                        try:
                            widgets[key].title(val)
                        except KeyError:
                            self.warn("Invalid SUBWINDOW: %s", key)

            elif kind in [WIDGET_NAMES.ListBox]:
                for k in widgets.keys():
                    lb = widgets[k]

                    # convert data to a list
                    if self.configParser.has_option(section, k):
                        data = self.configParser.get(section, k)
                    else:
                        data = lb.DEFAULT_TEXT
                    data = data.strip().split("\n")

                    # tidy up the list
                    data = [item.strip() for item in data if len(item.strip()) > 0]
                    self.updateListBox(k, data)

            elif kind in [WIDGET_NAMES.SpinBox]:
                for k in widgets.keys():
                    sb = widgets[k]

                    # convert data to a list
                    if self.configParser.has_option(section, k):
                        data = self.configParser.get(section, k)
                    else:
                        data = sb.DEFAULT_TEXT
                    data = data.strip().split("\n")

                    # tidy up the list
                    data = [item.strip() for item in data if len(item.strip()) > 0]
                    self.changeSpinBox(k, data)

            elif kind in [WIDGET_NAMES.OptionBox]:
                for k in widgets.keys():
                    ob = widgets[k]

                    # convert data to a list
                    if self.configParser.has_option(section, k):
                        data = self.configParser.get(section, k)
                    else:
                        data = ob.DEFAULT_TEXT
                    data = data.strip().split("\n")

                    # tidy up the list
                    data = [item.strip() for item in data if len(item.strip()) > 0]
                    self.changeOptionBox(k, data)

            elif kind in [WIDGET_NAMES.RadioButton]:
                for (key, val) in self.configParser.items(section):
                    gui.trace("\t\t%s: %s", key, val)
                    keys = key.split("-")
                    if len(keys) != 2:
                        self.warn("Invalid RADIOBUTTON key: %s", key)
                    else:
                        try:
                            rbs = self.widgetManager.get(WIDGET_NAMES.RadioButton, keys[0])
                        except KeyError:
                            self.warn("Invalid RADIOBUTTON key: %s", keys[0])
                            continue
                        for rb in rbs:
                            if rb.DEFAULT_TEXT == keys[1]:
                                rb["text"] = val
                                break

            elif kind in [WIDGET_NAMES.TabbedFrame]:
                for (key, val) in self.configParser.items(section):
                    gui.trace("\t\t%s: %s", key, val)
                    keys = key.split("-")
                    if len(keys) != 2:
                        self.warn("Invalid TABBEDFRAME key: %s", key)
                    else:
                        try:
                            self.setTabText(keys[0], keys[1], val)
                        except ItemLookupError:
                            self.warn("Invalid TABBEDFRAME: %s with TAB: %s" , keys[0], keys[1])

            elif kind in [WIDGET_NAMES.Properties]:
                for (key, val) in self.configParser.items(section):
                    gui.trace("\t\t%s: %s", key, val)
                    keys = key.split("-")
                    if len(keys) != 2:
                        self.warn("Invalid PROPERTIES key: %s", key)
                    else:
                        try:
                            self.setPropertyText(keys[0], keys[1], val)
                        except ItemLookupError:
                            self.warn("Invalid PROPERTIES: %s", keys[0])
                        except KeyError:
                            self.warn("Invalid PROPERTY: %s", keys[1])

            elif kind == WIDGET_NAMES.Tree:
                for (key, val) in self.configParser.items(section):
                    gui.trace("\t\t%s: %s", key, val)
                    keys = key.split("-")
                    if len(keys) != 2:
                        self.warn("Invalid GRID key: %s", key)
                    else:
                        if keys[1] not in ["actionHeading", "actionButton", "addButton"]:
                            self.warn("Invalid GRID label: %s for GRID: %s", keys[1], keys[0])
                        else:
                            try:
                                self.confGrid(keys[0], keys[1], val)
                            except ItemLookupError:
                                self.warn("Invalid GRID: %s", keys[0])

            elif kind == self.PAGEDWINDOW:
                for (key, val) in self.configParser.items(section):
                    gui.trace("\t\t%s: %s", key, val)
                    keys = key.split("-")
                    if len(keys) != 2:
                        self.warn("Invalid PAGEDWINDOW key: %s", key)
                    else:
                        if keys[1] not in ["prevButton", "nextButton", "title"]:
                            self.warn("Invalid PAGEDWINDOW label: %s for PAGEDWINDOW: %s", keys[1], keys[0])
                        else:
                            try:
                                widgets[keys[0]].config(**{keys[1]:val})
                            except KeyError:
                                self.warn("Invalid PAGEDWINDOW: %s", keys[0])

            elif kind == WIDGET_NAMES.Entry:
                for k in widgets.keys():
                    ent = widgets[k]

                    if self.configParser.has_option(section, k):
                        data = self.configParser.get(section, k)
                    else:
                        data = ent.DEFAULT_TEXT

                    gui.trace("\t\t%s: %s", k, data)
                    self.setEntryDefault(k, data)

            elif kind in [WIDGET_NAMES.Image]:
                for k in widgets.keys():
                    if self.configParser.has_option(section, k):
                        data = str(self.configParser.get(section, k))

                        try:
                            self.setImage(k, data)
                            gui.trace("\t\t%s: %s", k, data)
                        except:
                            self.error("Failed to update image: %s to: %s", k, data)
                    else:
                        gui.trace("No translation for: %s", k)

            elif kind in [WIDGET_NAMES.Label, WIDGET_NAMES.Button, WIDGET_NAMES.CheckBox, WIDGET_NAMES.Message,
                            WIDGET_NAMES.Link, WIDGET_NAMES.LabelFrame, self.TOGGLEFRAME]:
                for k in widgets.keys():
                    widg = widgets[k]

                    # skip validation labels - we don't need to translate them
                    try:
                        if kind == WIDGET_NAMES.Label and widg.isValidation:
                            gui.trace("\t\t%s: skipping, validation label", k)
                            continue
                    except:
                        pass

                    if self.configParser.has_option(section, k):
                        data = str(self.configParser.get(section, k))
                    else:
                        data = widg.DEFAULT_TEXT

                    gui.trace("\t\t%s: %s", k, data)
                    widg.config(text=data)

            elif kind == WIDGET_NAMES.Toolbar:
                for k in widgets.keys():
                    but = widgets[k]
                    if but.image is None:
                        if self.configParser.has_option(section, k):
                            data = str(self.configParser.get(section, k))
                        else:
                            data = but.DEFAULT_TEXT

                        gui.trace("\t\t%s: %s", k, data)
                        but.config(text = data)

            elif kind == "TOOLTIP":
                try:
                    kind = WIDGET_NAMES.name(WIDGET_NAMES.getIgnoreCase(section.split("-")[1]))
                    func = getattr(self, "set"+kind+"Tooltip")
                except KeyError:
                    self.warn("Invalid config section: TOOLTIP-%s", section)
                    return
                gui.trace("Parsing TOOLTIPs for: %s", kind)

                for (key, val) in self.configParser.items(section):
                    try:
                        func(key, val)
                    except ItemLookupError:
                        self.warn("Invalid TOOLTIP for: %s, with key: %s", kind, key)
                        continue
            else:
                self.warn("Unsupported widget: %s", section)
                continue

    language = property(getLanguage, changeLanguage)

    def showSplash(self, text="appJar", fill="#FF0000", stripe="#000000", fg="#FFFFFF", font=44):
        """ creates a splash screen to show at start up """
        self.splashConfig= {'text':text, 'fill':fill, 'stripe':stripe, 'fg':fg, 'font':font}


##################################################
### Stuff for logging
##################################################

    @staticmethod
    def setLogFile(fileName):
        """ sets the filename for logging messages """
        # Remove all handlers associated with the root logger object.
        for handler in logging.root.handlers[:]:
            logging.root.removeHandler(handler)
        logging.basicConfig(level=logging.INFO, filename=fileName, format='%(asctime)s %(name)s:%(levelname)s: %(message)s')
        gui.info("Switched to logFile: %s", fileName)

    def _setLogFile(self, fileName):
        ''' necessary so we can access this as a property '''
        gui.setLogFile(fileName)

    def getLogFile(self):
        return logging.root.handlers[0].baseFilename

    logFile = property(getLogFile, _setLogFile)

    @staticmethod
    def setLogLevel(level):
        """ main function for setting the logging level
            provide one of: INFO, DEBUG, WARNING, ERROR, CRITICAL, EXCEPTION, None """
        logging.getLogger("appJar").setLevel(getattr(logging, level.upper()))
        gui.info("Log level changed to: %s", level)


    def getLogLevel(self):
        return logging.getLevelName(logging.getLogger("appJar").getEffectiveLevel())

    def _setLogLevel(self, level):
        ''' necessary so we can access this as a property '''
        gui.setLogLevel(level)

    logLevel = property(getLogLevel, _setLogLevel)

    @staticmethod
    def exception(message, *args):
        """ wrapper for logMessage - setting level to EXCEPTION """
        gui.logMessage(message, "EXCEPTION", *args)

    @staticmethod
    def critical(message, *args):
        """ wrapper for logMessage - setting level to CRITICAL """
        gui.logMessage(message, "CRITICAL", *args)

    @staticmethod
    def error(message, *args):
        """ wrapper for logMessage - setting level to ERROR """
        gui.logMessage(message, "ERROR", *args)

    @staticmethod
    def warn(message, *args):
        """ wrapper for logMessage - setting level to WARNING """
        gui.logMessage(message, "WARNING", *args)

    @staticmethod
    def debug(message, *args):
        """ wrapper for logMessage - setting level to DEBUG """
        gui.logMessage(message, "DEBUG", *args)

    @staticmethod
    def trace(message, *args):
        """ wrapper for logMessage - setting level to TRACE """
        gui.logMessage(message, "TRACE", *args)

    @staticmethod
    def info(message, *args):
        """ wrapper for logMessage - setting level to INFO """
        gui.logMessage(message, "INFO", *args)

    @staticmethod
    def logMessage(msg, level, *args):
        """ allows user to log a message - provide a message and a log level
            any %s tags in the message will be replaced by the relevant positional *args """
        frames = inspect.stack()
        # try to ensure we only log extras if we're called from above functions
        if frames[1][3] in ("exception", "critical", "error", "warn", "debug", "trace", "info"):

            callFrame = ""
            try:
                progName = gui.exe_file
                for s in frames:
                    if progName in s[1]:
                        callFrame = s
                        break
            except: pass

            if callFrame != "":
                callFrame = "Line " + str(callFrame[2])

            # user generated call
            if "appjar.py" not in frames[2][1] or frames[2][3] == "handlerFunction":
                if callFrame != "":
                    msg = "[" + callFrame + "]: "+str(msg)

            # appJar logging
            else:
                if callFrame != "":
                    msg = "["+callFrame + "->" + str(frames[2][2]) +"/"+str(frames[2][3])+"]: "+str(msg)
                else:
                    msg = "["+str(frames[2][2]) +"/"+str(frames[2][3])+"]: "+str(msg)

        logger = logging.getLogger("appJar")
        level = level.upper()

        if level == "EXCEPTION": logger.exception(msg, *args)
        elif level == "CRITICAL": logger.critical(msg, *args)
        elif level == "ERROR": logger.error(msg, *args)
        elif level == "WARNING": logger.warning(msg, *args)
        elif level == "INFO": logger.info(msg, *args)
        elif level == "DEBUG": logger.debug(msg, *args)
        elif level == "TRACE": logger.trace(msg, *args)

##############################################################
# Event Loop - must always be called at end
##############################################################
    def __enter__(self):
        """ allows gui to be used as a ContextManager """
        gui.trace("ContextManager: initialised")
        return self

    def __exit__(self, eType, eValue, eTrace):
        """ allows gui to be used as a ContextManager
            - calls the go() function """
        if eType is not None:
            self.error("ContextManager failed: %s", eValue)
            return False
        else:
            gui.trace("ContextManager: starting")
            self.go(startWindow=self.startWindow)
            return True

    def go(self, language=None, startWindow=None):
        """ Most important function! starts the GUI """

        # check if we have a command line language
        if self._language is not None:
            language = self._language

        # if language is populated, we are in internationalisation mode
        # call the changeLanguage function - to re-badge all the widgets
        if language is not None:
            self.changeLanguage(language)

        if self.splashConfig is not None:
            gui.trace("SPLASH: %s", self.splashConfig)
            splash = SplashScreen(
                            self.topLevel,
                            self.splashConfig['text'],
                            self.splashConfig['fill'],
                            self.splashConfig['stripe'],
                            self.splashConfig['fg'],
                            self.splashConfig['font']
                            )
            self.topLevel.withdraw()
            self._bringToFront(splash)

        # check the containers have all been stopped
        if len(self.containerStack) > 1:
            for i in range(len(self.containerStack) - 1, 0, -1):
                kind = self.containerStack[i]['type']
                if kind != WIDGET_NAMES.Pane:
                    self.warn("No stopContainer called on: %s", WIDGET_NAMES.name(kind))

        # update any trees
        for k in self.widgetManager.group(WIDGET_NAMES.Tree):
            self.generateTree(k)

        # create appJar menu, if no menuBar created
        if not self.hasMenu:
            self.addAppJarMenu()

        if self.platform == self.WINDOWS:
            self.menuBar.add_cascade(menu=self.widgetManager.get(WIDGET_NAMES.Menu, "WIN_SYS"))
        self.topLevel.config(menu=self.menuBar)

        if startWindow is not None:
            self.startWindow = startWindow
            gui.trace("startWindow parameter: %s", startWindow)

        # pack it all in & make sure it's drawn
        self.appWindow.pack(fill=BOTH)
        if self.useSettings:
            self.loadSettings(self.settingsFile)
        self.topLevel.update_idletasks()

        # check geom is set and set a minimum size, also positions the window if necessary
        if not self.topLevel.locationSet:
            self.setLocation('CENTER')

        if not hasattr(self.topLevel, 'ms'):
            self.setMinSize()

        if self.splashConfig is not None:
            time.sleep(3)
            splash.destroy()

        # user hasn't specified anything
        if self.startWindow is None:
            if not self.topLevel.displayed:
                gui.trace("topLevel has been manually hidden - not showing in go()")
            else:
                gui.trace("Showing topLevel")
                self._bringToFront()
                self.topLevel.deiconify()
        else:
            gui.trace("hiding main window")
            self.hide()
            sw = self.widgetManager.get(WIDGET_NAMES.SubWindow, startWindow)
            if sw.blocking:
                raise Exception("Unable to start appjar with a blocking subWindow")

            self.showSubWindow(startWindow)

        # required to make the gui reopen after minimising
        if self.GET_PLATFORM() == self.MAC:self.topLevel.createcommand('tk::mac::ReopenApplication', self._macReveal)

        # start the call back & flash loops
        self._poll()
        self._flash()

        # register start-up function
        if self.topLevel.startFunction is not None:
            self.topLevel.after_idle(self.topLevel.startFunction)

        # start the main loop
        try:
            self.topLevel.mainloop()
        except(KeyboardInterrupt, SystemExit) as e:
            gui.trace("appJar stopped through ^c or exit()")
            self.stop()
        except Exception as e:
            self.exception(e)
            self.stop()

    def setStartFunction(self, func):
        f = self.MAKE_FUNC(func, "start")
        self.topLevel.startFunction = f

    startFunction = property(fset=setStartFunction)

    def _macReveal(self):
        """ internal function to deiconify GUIs on mac """
        if self.topLevel.state() != "withdrawn":
            self.topLevel.deiconify()
        for k, v in self.widgetManager.group(WIDGET_NAMES.SubWindow).items():
            if v.state() == "normal":
                self.showSubWindow(k)

    def setStopFunction(self, function):
        """ Set a function to call when the GUI is quit. Must return True or False """
        tl = self._getTopLevel()
        tl.stopFunction = function
        # link to exit item in topMenu
        # only if in root
        if self._getContainerProperty('type') != WIDGET_NAMES.SubWindow:
            tl.createcommand('exit', self.stop)

    stopFunction = property(fset=setStopFunction)

    def setSetting(self, name, value):
        """ adds a setting to the settings file """
        self.externalSettings[name] = value

    def getSetting(self, name, default=None):
        """ gets a setting form the settings file """
        try: return self.externalSettings[name]
        except: return default

    def saveSettings(self, fileName="appJar.ini"):
        """ saves the current settings to a file
            called automatically by stop() of settings were loaded at start """
        self._loadConfigParser()
        if not ConfigParser:
            self.error("Unable to save config file - no configparser")
            return

        settings = ConfigParser()
        settings.optionxform = str
        settings.add_section('GEOM')
        geom = self.topLevel.geometry()
        ms = self.topLevel.minsize()
        ms = "%s,%s" % (ms[0], ms[1])
        settings.set('GEOM', 'geometry', geom)
        gui.trace("Save geom as: %s", geom)
        settings.set('GEOM', 'minsize', ms)
        settings.set('GEOM', "fullscreen", str(self.topLevel.attributes('-fullscreen')))
        settings.set('GEOM', "state", str(self.topLevel.state()))

        # get toolbar setting
        if self.tb.inUse:
            gui.trace("Saving toolbar settings")
            settings.add_section("TOOLBAR")
            settings.set("TOOLBAR", "pinned", str(self.tb.pinned))

        # get container settings
        for k, v in self.widgetManager.group(WIDGET_NAMES.ToggleFrame).items():
            gui.trace("Saving toggle %s", k)
            if "TOGGLES" not in settings.sections(): settings.add_section("TOGGLES")
            settings.set("TOGGLES", k, str(v.isShowing()))

        for k, v in self.widgetManager.group(WIDGET_NAMES.TabbedFrame).items():
            gui.trace("Saving tab %s", k)
            if "TABS" not in settings.sections(): settings.add_section("TABS")
            settings.set("TABS", k, str(v.getSelectedTab()))

        for k, v in self.widgetManager.group(WIDGET_NAMES.PagedWindow).items():
            gui.trace("Saving page %s", k)
            if "PAGES" not in settings.sections(): settings.add_section("PAGES")
            settings.set("PAGES", k, str(v.getPageNumber()))

        for k, v in self.widgetManager.group(WIDGET_NAMES.SubWindow).items():
            if "SUBWINDOWS" not in settings.sections(): settings.add_section("SUBWINDOWS")
            if v.shown:
                v.update()
                settings.set("SUBWINDOWS", k, "True")
                settings.add_section(k)
                settings.set(k, "geometry", v.geometry())
                ms = v.minsize()
                settings.set(k, 'minsize', "%s,%s" % (ms[0], ms[1]))
                settings.set(k, "state", v.state())
                gui.trace("Saving subWindow %s: geom=%s, state=%s, minsize=%s", k, v.geometry(), v.state(), ms)
            else:
                settings.set("SUBWINDOWS", k, "False")
                gui.trace("Skipping subwindow: %s", k)

        for k, v in self.externalSettings.items():
            if "EXTERNAL" not in settings.sections(): settings.add_section("EXTERNAL")
            settings.set("EXTERNAL", k, str(v))

        # pane positions?
        # sub windows geom & visibility
        # scrollpane x & y positions
        # language
        # ttk
        # debug level

        with open(fileName, 'w') as theFile:
            settings.write(theFile)

    def loadSettings(self, fileName="appJar.ini", useSettings=True):
        """ loads setting from a settings file, and adjusts the GUI to match
            called by go() function, if user has requested settings """
        self._loadConfigParser()
        if not ConfigParser:
            self.error("Unable to save config file - no configparser")
            return

        self.useSettings = useSettings

        settings = ConfigParser()
        settings.optionxform = str
        settings.read(fileName)

        if settings.has_option("GEOM", "geometry"):
            geom = settings.get("GEOM", "geometry")
            if not self.topLevel.ignoreSettings:
                size, loc = gui.SPLIT_GEOM(geom)
                gui.trace("Setting topLevel geom: %s as size: %s, loc: %s", geom, size, loc)
                if size[0] > 1:
                    self.setSize(*size)
                if loc[0] != -1:
                    self.setLocation(*loc)
            else:
                gui.trace("Ignoring topLevel geom: %s", geom)

        # not finished
        if settings.has_option("GEOM", "fullscreen"):
            fs = settings.getboolean('GEOM', "fullscreen")
            gui.trace("Set fullscreen to: %s", fs)
            if fs: self.setFullscreen()
            else: self.exitFullscreen()

        if settings.has_option("GEOM", "minsize"):
            self.topLevel.ms = settings.get('GEOM', "minsize").split(",")
            self._getTopLevel().minsize(self.topLevel.ms[0], self.topLevel.ms[1])
            gui.trace("Set minsize to: %s", self.topLevel.ms)

        if settings.has_option("GEOM", "state"):
            state = settings.get('GEOM', "state")
            if state in ["withdrawn", "zoomed"]:
                self._getTopLevel().state(state)

        if settings.has_option("TOOLBAR", "pinned") and self.tb.inUse:
            tb = settings.getboolean("TOOLBAR", "pinned")
            self.setToolbarPinned(tb)
            gui.trace("Set toolbar to: %s", tb)

        if "TOGGLES" in settings.sections():
            for k in settings.options("TOGGLES"):
                try:
                    if self.getToggleFrameState(k) != settings.getboolean("TOGGLES", k):
                        self.toggleToggleFrame(k)
                except ItemLookupError:
                    gui.error("Settings error, invalid TOGGLES name: %s - discarding", k)

        if "TABS" in settings.sections():
            for k in settings.options("TABS"):
                try:
                    self.setTabbedFrameSelectedTab(k, settings.get("TABS", k))
                except ItemLookupError:
                    gui.error("Settings error, invalid TABS name: %s - discarding", k)

        if "PAGES" in settings.sections():
            for k in settings.options("PAGES"):
                try:
                    self.setPagedWindowPage(k, settings.getint("PAGES", k))
                except ItemLookupError:
                    gui.error("Settings error, invalid PAGES name: %s - discarding", k)

        if "SUBWINDOWS" in settings.sections():
            for k in settings.options("SUBWINDOWS"):
                if settings.getboolean("SUBWINDOWS", k):
                    gui.trace("Loading settings for %s", k)
                    try:
                        tl = self.widgetManager.get(WIDGET_NAMES.SubWindow, k)
                        # process the geom settings
                        if settings.has_option(k, "geometry"):
                            geom = settings.get(k, "geometry")
                            size, loc = gui.SPLIT_GEOM(geom)
                            if size[0] > 1:
                                gui.trace("Setting size: %s", size)
                                tl.geometry("%sx%s" % (size[0], size[1]))
                                tl.shown = True
                            else:
                                gui.trace("Skipping size: %s", size)
                            if loc[0] > -1:
                                gui.trace("Setting location: %s", loc)
                                self.setSubWindowLocation(k, *loc)
                            else:
                                gui.trace("Skipping location: %s", loc)
                        else:
                            gui.trace("No location found")

                        if settings.has_option(k, "minsize"):
                            ms = settings.get(k, "minsize").split(",")
                            self.setMinSize(tl, ms)

                        # set the state - if there' no startWindow
                        if self.startWindow is None:
                            try:
                                tl.state(settings.get(k, "state"))
                                gui.trace("Set state=%s", tl.state())
                            except: pass # no state found
                    except ItemLookupError:
                        gui.error("Settings error, invalid SUBWINDOWS name: %s - discarding.", k)
                else:
                    gui.trace("Skipping settings for %s", k)

        if "EXTERNAL" in settings.sections():
            for k in settings.options("EXTERNAL"):
                self.externalSettings[k] = settings.get("EXTERNAL", k)

    def stop(self, event=None):
        """ Closes the GUI. If a stop function is set, will only close the GUI if True """
        theFunc = self._getTopLevel().stopFunction
        if theFunc is None or theFunc():

            if self.useSettings:
                self.saveSettings(self.settingsFile)

            # stop the after loops
            self.alive = False
            self.topLevel.after_cancel(self.pollId)
            self.topLevel.after_cancel(self.flashId)
            if self.preloadAnimatedImageId:
                self.topLevel.after_cancel(self.preloadAnimatedImageId)
            if self.processQueueId:
                self.topLevel.after_cancel(self.processQueueId)

            # stop any animations
            for key in self.widgetManager.group(WIDGET_NAMES.AnimationID):
                self.topLevel.after_cancel(self.widgetManager.get(WIDGET_NAMES.AnimationID, key))

            # stop any maps
            for key in self.widgetManager.group(WIDGET_NAMES.Map):
                self.widgetManager.get(WIDGET_NAMES.Map, key).stopUpdates()

            # stop any sounds, ignore error when not on Windows
            try:
                self.stopSound()
            except:
                pass

            self.topLevel.quit()
            if not self.fastStop: self.topLevel.destroy()
            self.__class__.instantiated = False
            gui.info("--- GUI stopped ---")

    def setFastStop(self, fast=True):
        self._fastStop = fast

    def getFastStop(self):
        return self._fastStop

    fastStop = property(getFastStop, setFastStop)

#####################################
# Functions for configuring polling events
#####################################
    def setPollTime(self, time):
        """ Set a frequency for executing queued functions
            events will fire in order of being added, after sleeping for time """
        self.pollTime = time

    def registerEvent(self, func):
        """ Queue a function, to be executed every poll time """
        self.events.append(func)

    def after(self, delay_ms, callback=None, *args):
        """ wrapper for topLevel after function
            schedules the callback function to happen in x seconds
            returns an ID, allowing the event to be cancelled """
        return self.topLevel.after(delay_ms, callback, *args)

    def afterIdle(self, callback, *args):
        """ wrapper for topLevel after_idle function
            schedules the callback function to happen in x seconds
            returns an ID, allowing the event to be cancelled """
        return self.after_idle(callback, *args)

    def after_idle(self, callback, *args):
        """ wrapper for topLevel after_idle function
            schedules the callback function to happen in x seconds
            returns an ID, allowing the event to be cancelled """
        return self.topLevel.after_idle(callback, *args)

    def afterCancel(self, afterId):
        """ wrapper for topLevel after_cancel function
            tries to cancel the specified callback """
        return self.after_cancel(afterId)

    def after_cancel(self, afterId):
        """ wrapper for topLevel after_cancel function
            tries to cancel the specified callback """
        return self.topLevel.after_cancel(afterId)

    def queueFunction(self, func, *args, **kwargs):
        """ adds the specified function & arguments to the event queue
        Functions in the event queue are actioned by the gui's main thread

        :param func: the function to call
        :param *args: any number of ordered arguments
        :param **kwargs: any number of named arguments
        :raises Full: if unable to add the function to the queue
        """
        self._loadThreading()
        if Queue is False:
            gui.warn("Unable to queueFunction - threading not possible.")
        else:
            self.eventQueue.put((5, func, args, kwargs), block=False)

    def queuePriorityFunction(self, func, *args, **kwargs):
        """ queues the function with a higher priority - not working yet """
        self._loadThreading()
        if Queue is False:
            gui.warn("Unable to queueFunction - threading not possible.")
        else:
            self.eventQueue.put((1, func, args, kwargs), block=False)

    def _processEventQueue(self):
        """ internal function to process events in the event queue
            put there by queue function """
        if not self.alive: return
        if not self.eventQueue.empty():
            priority, func, args, kwargs = self.eventQueue.get()
            gui.trace("FUNCTION: %s(%s)", func, args)
            func(*args, **kwargs)

        self.processQueueId = self.after(self.EVENT_SPEED, self._processEventQueue)

    def thread(self, func, *args, **kwargs):
        """ will run the supplied function in a separate thread

        param func: the function to run
        """
        self._loadThreading()
        if Queue is False:
            gui.warn("Unable to queueFunction - threading not possible.")
        else:
            t = Thread(group=None, target=func, name=None, args=args, kwargs=kwargs)
            t.daemon = True
            t.start()

    def callback(self, *args, **kwargs):
        """Shortner for threadCallback."""
        return self.threadCallback(*args, **kwargs)

    def threadCallback(self, func, callback, *args, **kwargs):
        """Run a given method in a new thread with passed arguments.
           When func completes call the callback with the result.

           :param func: Method that returns the result.
           :param callback: Method that receives the result.
           :param args: Positional arguments for func.
           :param kwargs: Keyword args for func.
        """
        def innerThread(func, callback, *args, **kwargs):
            result = func(*args, **kwargs)
            self.queueFunction(callback, result)

        if not callable(func) or not callable(callback):
            gui.error("Function (or callback) method isn't callable!")
            return
        self.thread(innerThread, func, callback, *args, **kwargs)

    # internal function, called by 'after' function, after sleeping
    def _poll(self):
        """ internal function, called by 'after' function, after sleeping """
        if not self.alive: return
        # run any registered actions
        for e in self.events:
            # execute the event
            e()
        self.pollId = self.topLevel.after(self.pollTime, self._poll)

    def _windowEvent(self, event):
        """ called whenever the GUI updates - does nothing """
        new_width = self.topLevel.winfo_width()
        new_height = self.topLevel.winfo_height()

    def enableEnter(self, func, replace=False):
        """ Binds <Return> to the specified function - all widgets """
        self.bindKey("Return", func, replace)

    def disableEnter(self):
        """ unbinds <Return> from all widgets """
        self.unbindKey("Return")

    def _enterWrapper(self, func):
        if func is None:
            self.disableEnter()
        else:
            self.enableEnter(func, replace=True)

    enterKey = property(fset=_enterWrapper)

    def bindKeys(self, keys, func):
        """ bind the specified keys, to the specified function, for all widgets """
        for key in keys:
            self.bindKey(key, func)

    def bindKey(self, key, func, replace=False):
        """ bind the specified key, to the specified function, for all widgets """
        if replace:
            try: self.unbindKey(key)
            except: pass
        # for now discard the Event...
        myF = self.MAKE_FUNC(func, key)
        binding = EventBinding(key, myF, self._getTopLevel(), menuBinding=False)
        try:
            self.widgetManager.add(WIDGET_NAMES.Bindings, binding.displayName, binding)
            binding.createBindings()
        except ItemLookupError:
            raise ItemLookupError('Unable to bind key ' + binding.displayName + ' - binding already exists')

    def unbindKeys(self, keys):
        """ unbinds the specified keys from whatever functions they are bound to """
        for key in keys:
            self.unbindKey(key)

    def unbindKey(self, key):
        """ unbinds the specified key from whatever functions it is bound to """
        if key[0] == "<":
            gui.warn("Shortcuts should not include chevrons: %s", key)
            key= key[1:-1]
        self.widgetManager.get(WIDGET_NAMES.Bindings, key).removeBindings()
        self.widgetManager.remove(WIDGET_NAMES.Bindings, key)

    def _isMouseInWidget(self, w):
        """ helper - returns True if the mouse is in the specified widget """
        l_x = w.winfo_rootx()
        l_y = w.winfo_rooty()

        if l_x <= w.winfo_pointerx() <= l_x + \
                w.winfo_width() and l_y <= w.winfo_pointery() <= l_y + w.winfo_height():
            return True
        else:
            return False

    # function to give a clicked widget the keyboard focus
    def _grabFocus(self, e):
        """ gives the specified widget the focus """
        e.widget.focus_set()


#####################################
# FUNCTIONS for configuring GUI settings
#####################################

    def setSize(self, geom, height=None, ignoreSettings=None):
        """ called to update screen geometry
            can take a geom string, or a width & height
            can override ignoreSettings if desired """
        container = self._getTopLevel()
        if ignoreSettings is not None:
            container.ignoreSettings = ignoreSettings

        try:
            geom = geom.lower()
        except:
            # ignore - other data types allowed
            pass

        if geom == "fullscreen":
            self.setFullscreen()
        elif geom is not None:
            if height is not None:
                geom=(geom, height)
            elif not isinstance(geom, list) and not isinstance(geom, tuple):
                geom, loc = gui.SPLIT_GEOM(geom)

            size = "%sx%s" % (int(geom[0]), int(geom[1]))
            gui.trace("Setting size: %s", size)

            # warn the user that their geom is not big enough
            dims = gui.GET_DIMS(container)
            if geom[0] < dims["b_width"] or geom[1] < dims["b_height"]:
                self.trace("Specified dimensions (%s, %s) less than requested dimensions (%s, %s)",
                        geom[0], geom[1], dims["b_width"], dims["b_height"])

            # and set it as the minimum size
            if not hasattr(container, 'ms'):
                self.setMinSize(container, geom)

            self.exitFullscreen()
            container.geometry(size)

    def getSize(self):
        container = self._getTopLevel()
        size, loc = gui.SPLIT_GEOM(container.geometry())
        return size

    size = property(getSize, setSize)

    def setMinSize(self, container=None, size=None):
        """ sets a minimum size for the specified container - defaults to the whole GUI """
        if container is None: container = self.topLevel
        if size is None: size = (gui.GET_DIMS(container)["r_width"], gui.GET_DIMS(container)["r_height"])
        container.ms = size
        container.minsize(size[0], size[1])
        gui.trace("Minsize set to: %s", size)

    def setLocation(self, x, y=None, ignoreSettings=None, win=None, up=0):
        """ called to set the GUI's position on screen """
        if win is None:
            win = self._getTopLevel()
        gui.SET_LOCATION(x, y, ignoreSettings, win, up)

    def getLocation(self):
        container = self._getTopLevel()
        size, loc = gui.SPLIT_GEOM(container.geometry())
        return loc

    location = property(getLocation, setLocation)

    def _bringToFront(self, win=None):
        """ called to make sure this window is on top of other windows """
        if win is None:
            win = self.topLevel
            top = self.top
        else:
            top = win.attributes('-topmost')

        if self.platform == self.MAC:
            import subprocess
            tmpl = 'tell application "System Events" to set frontmost of every process whose unix id is {0} to true'
            script = tmpl.format(os.getpid())
            subprocess.check_call(['/usr/bin/osascript', '-e', script])
            win.after( 0, lambda: win.attributes("-topmost", top))
#            val=os.system('''/usr/bin/osascript -e 'tell app "Finder" to set frontmost of process "''' + PY_NAME + '''" to true' ''')
            win.lift()
        elif self.platform == self.WINDOWS:
            win.lift()
        elif self.platform == self.LINUX:
            win.lift()

    def setFullscreen(self, title=None):
        """ sets the specified window to be fullscreen
            if no title, will set the main GUI """
        try:
            container = self.widgetManager.get(WIDGET_NAMES.SubWindow, title)
        except:
            container = self._getTopLevel()

        if not container.isFullscreen:
            container.isFullscreen = True
            container.attributes('-fullscreen', True)
            container.escapeBindId = container.bind('<Escape>', self.MAKE_FUNC(self.exitFullscreen, container), "+")

    def getFullscreen(self, title=None):
        if title is None:
            container = self._getTopLevel()
        else:
            container = self.widgetManager.get(WIDGET_NAMES.SubWindow, title)

        return container.isFullscreen

    def setOnTop(self, stay=True):
        self._getTopLevel().attributes("-topmost", stay)
        gui.trace("Staying on top set to: %s", stay)

    def getOnTop(self):
        return self._getTopLevel().attributes("-topmost") == 1

    top = property(getOnTop, setOnTop)

    def _changeFullscreen(self, flag):
        if flag: self.setFullscreen()
        else: self.exitFullscreen()

    fullscreen = property(getFullscreen, _changeFullscreen)

    def exitFullscreen(self, container=None):
        """ turns off fullscreen mode for the specified window """
        if container is None or isinstance(container, UNIVERSAL_STRING):
            try:
                container = self.widgetManager.get(WIDGET_NAMES.SubWindow, container)
            except:
                container = self._getTopLevel()

        if container.isFullscreen:
            container.isFullscreen = False
            container.attributes('-fullscreen', False)
            if container.escapeBindId is not None:
                container.unbind('<Escape>', container.escapeBindId)
            with PauseLogger():
                self._doTitleBar()
            return True
        else:
            return False

    def setPadX(self, x=0):
        """ set the current container's external grid padding """
        self.containerStack[-1]['padx'] = x

    def setPadY(self, y=0):
        """ set the current container's external grid padding """
        self.containerStack[-1]['pady'] = y

    def setPadding(self, x, y=None):
        """ sets the padding around the border of the current container """
        x, y = gui.PARSE_TWO_PARAMS(x, y)
        self.containerStack[-1]['padx'] = x
        self.containerStack[-1]['pady'] = y

    def getPadding(self):
        return self._getContainerProperty('padx'), self._getContainerProperty('pady')

    padding = property(getPadding, setPadding)

    def config(self, **kwargs):
        self.configure(**kwargs)

    def configure(self, **kwargs):
        title = kwargs.pop("title", None)
        icon = kwargs.pop("icon", None)
        transparency = kwargs.pop("transparency", None)
        visible = kwargs.pop("visible", None)
        top = kwargs.pop("top", None)

        padding = kwargs.pop("padding", None)
        inPadding = kwargs.pop("inPadding", None)
        guiPadding = kwargs.pop("guiPadding", None)

        size = kwargs.pop("size", None)
        location = kwargs.pop("location", None)
        fullscreen = kwargs.pop("fullscreen", None)
        resizable = kwargs.pop("resizable", None)

        sticky = kwargs.pop("sticky", None)
        stretch = kwargs.pop("stretch", None)
        expand = kwargs.pop("expand", None)
        row = kwargs.pop("row", None)
        colspan = kwargs.pop("colspan", None)
        rowspan = kwargs.pop("rowspan", None)

        fg = kwargs.pop("fg", None)
        bg = kwargs.pop("bg", None)
        font = kwargs.pop("font", None)
        buttonFont = kwargs.pop("buttonFont", None)
        labelFont = kwargs.pop("labelFont", None)
        inputFont = kwargs.pop("inputFont", None)
        statusFont = kwargs.pop("statusFont", None)
        ttkTheme = kwargs.pop("ttkTheme", None)

        editMenu = kwargs.pop("editMenu", None)
        # two possible names
        stopFunction = kwargs.pop("stop", kwargs.pop("stopFunction", None))
        startFunction = kwargs.pop("start", kwargs.pop("startFunction", None))
        fastStop = kwargs.pop("fastStop", None)
        enterKey = kwargs.pop("enterKey", None)
        logLevel = kwargs.pop("log", kwargs.pop("logLevel", None))
        logFile = kwargs.pop("file", kwargs.pop("logFile", None))
        language = kwargs.pop("language", None)

        for k, v in kwargs.items():
            gui.error("Invalid config parameter: %s, %s", k, v)

        if title is not None: self.title = title
        if icon is not None: self.icon = icon
        if transparency is not None: self.transparency = transparency
        if visible is not None: self.visible = visible
        if top is not None: self.top = top

        if padding is not None: self.padding = padding
        if inPadding is not None: self.inPadding = inPadding
        if guiPadding is not None: self.guiPadding = guiPadding

        if size is not None: self.size = size
        if location is not None: self.location = location
        if fullscreen is not None: self.fullscreen = fullscreen
        if resizable is not None: self.resizable = resizable

        if sticky is not None: self.sticky = sticky
        if expand is not None: self.expand = expand
        if stretch is not None: self.stretch = stretch
        if row is not None: self.row = row
        if rowspan is not None: self.rowspan = rowspan
        if colspan is not None: self.colspan = colspan

        if fg is not None: self.fg = fg
        if bg is not None: self.bg = bg

        if font is not None: self.font = font
        if labelFont is not None: self.labelFont = labelFont
        if buttonFont is not None: self.buttonFont = buttonFont
        if inputFont is not None: self.inputFont = inputFont
        if statusFont is not None: self.statusFont = statusFont
        if ttkTheme is not None: self.ttkTheme = ttkTheme

        if editMenu is not None: self.editMenu = editMenu
        if stopFunction is not None: self.stopFunction = stopFunction
        if startFunction is not None: self.startFunction = startFunction
        if fastStop is not None: self.fastStop = fastStop
        if enterKey is not None: self.enterKey = enterKey
        if logLevel is not None: self.logLevel = logLevel
        if logFile is not None: self.logFile = logFile
        if language is not None: self.language = language

    def setGuiPadding(self, x, y=None):
        """ sets the padding around the border of the GUI """
        x, y = gui.PARSE_TWO_PARAMS(x, y)
        self.containerStack[0]['container'].config(padx=x, pady=y)

    def getGuiPadding(self):
        return int(str(self.containerStack[0]['container'].cget('padx'))), int(str(self.containerStack[0]['container'].cget('pady')))

    guiPadding = property(getGuiPadding, setGuiPadding)

    # sets the current containers internal padding
    def setIPadX(self, x=0):
        self.setInPadX(x)

    def setIPadY(self, y=0):
        self.setInPadY(y)

    def setIPadding(self, x, y=None):
        self.setInPadding(x, y)

    def setInPadX(self, x=0):
        self.containerStack[-1]['ipadx'] = x

    def setInPadY(self, y=0):
        self.containerStack[-1]['ipady'] = y

    def setInPadding(self, x, y=None):
        x, y = gui.PARSE_TWO_PARAMS(x, y)
        self.containerStack[-1]['ipadx'] = x
        self.containerStack[-1]['ipady'] = y

    def getInPadding(self):
        return self._getContainerProperty('ipadx'), self._getContainerProperty('ipady')

    inPadding = property(getInPadding, setInPadding)

    # set an override sticky for this container
    def setSticky(self, sticky):
        self.containerStack[-1]['sticky'] = sticky

    def getSticky(self):
        return self._getContainerProperty('sticky')

    # property for setTitle
    sticky = property(getSticky, setSticky)

    # this tells widgets what to do when GUI is resized
    def setStretch(self, exp):
        self.setExpand(exp)

    def getStretch(self):
        return self.getExpand()

    stretch = property(getStretch, setStretch)

    def getExpand(self):
        return self._getContainerProperty('expand')

    def setExpand(self, exp):
        if exp is None or exp.lower() == "none":
            self.containerStack[-1]['expand'] = "NONE"
        elif exp.lower() == "row":
            self.containerStack[-1]['expand'] = "ROW"
        elif exp.lower() == "column":
            self.containerStack[-1]['expand'] = "COLUMN"
        else:
            self.containerStack[-1]['expand'] = "ALL"

    expand = property(getExpand, setExpand)

    def RANDOM_COLOUR(self):
        return self.getRandomColour()

    def getRandomColour(self):
        """ generates a random colour """
        self._loadRandom()
        de=("%02x"%random.randint(0,255))
        re=("%02x"%random.randint(0,255))
        we=("%02x"%random.randint(0,255))
        return "#"+de+re+we

    randomColour = property(getRandomColour)

    def getFonts(self):
        fonts = list(tkFont.families())
        fonts.sort()
        return fonts

    fonts = property(getFonts)

    def increaseFont(self):
        self.increaseLabelFont()
        self.increaseButtonFont()

    def decreaseFont(self):
        self.decreaseLabelFont()
        self.decreaseButtonFont()

    def increaseButtonFont(self):
        self.setButtonFont(size=self._buttonFont['size'] + 1)

    def decreaseButtonFont(self):
        self.setButtonFont(size=self._buttonFont['size'] - 1)

    def increaseLabelFont(self):
        self.setLabelFont(size=self._labelFont['size'] + 1)

    def decreaseLabelFont(self):
        self.setLabelFont(size=self._labelFont['size'] - 1)

    def setFont(self, *args, **kwargs):
        self.setInputFont(*args, **kwargs)
        self.setLabelFont(*args, **kwargs)
        self.setButtonFont(*args, **kwargs)

    def getFont(self):
        return self._getContainerProperty('labelFont').actual()

    font = property(getFont, setFont)

    def _fontHelper(self, font, *args, **kwargs):
        if len(args) > 0:
            if isinstance(args[0], int):
                kwargs={'size':args[0]}
            elif isinstance(args[0], dict):
                kwargs=args[0]
            elif isinstance(args[0], tkFont.Font):
                gui.trace("%s set to new object", font)
                if font != "statusFont": self.containerStack[-1][font]=args[0]
                else: self._statusFont=args[0]
                return None
        if font != "statusFont":
            self._getContainerProperty(font).config(**kwargs)
            f = self._getContainerProperty(font).actual()['family'].lower()
        else:
            self._statusFont.config(**kwargs)
            f = self._statusFont.actual()['family'].lower()

        if 'family' in kwargs and kwargs['family'].lower() != f:
            gui.error("Failed to adjust %s to %s.", font, kwargs['family'])
        return kwargs

    def setInputFont(self, *args, **kwargs):
        self._fontHelper('inputFont', *args, **kwargs)

    def getInputFont(self):
        return self._getContainerProperty('inputFont').actual()

    inputFont = property(getInputFont, setInputFont)

    def setStatusFont(self, *args, **kwargs):
        self._fontHelper('statusFont', *args, **kwargs)

    def getStatusFont(self):
        return self._statusFont.actual()

    statusFont = property(getStatusFont, setStatusFont)

    def setButtonFont(self, *args, **kwargs):
        self._fontHelper('buttonFont', *args, **kwargs)

    def getButtonFont(self):
        return self._getContainerProperty('buttonFont').actual()

    buttonFont = property(getButtonFont, setButtonFont)

    def setLabelFont(self, *args, **kwargs):
        kwargs = self._fontHelper('labelFont', *args, **kwargs)
        if kwargs is not None:
            self.tableFont.config(**kwargs)

            # need better way to register font change events on tables
            for k, v in self.widgetManager.group(WIDGET_NAMES.Table).items():
                v.config(font=self.tableFont)

            linkArgs = kwargs.copy()
            linkArgs['underline'] = True
            linkArgs['weight'] = 'bold'
            self._linkFont.config(**linkArgs)

    def getLabelFont(self):
        return self._getContainerProperty('labelFont').actual()

    labelFont = property(getLabelFont, setLabelFont)

    # need to set a default colour for container
    # then populate that field
    # then use & update that field accordingly
    # all widgets will then need to use it
    # and here we update all....
    def setFg(self, colour, override=False):
        if not self.ttkFlag:
            self.containerStack[-1]['fg']=colour
            gui.SET_WIDGET_FG(self._getContainerProperty('container'), colour, override)

            for child in self._getContainerProperty('container').winfo_children():
                if not self._isWidgetContainer(child):
                    gui.SET_WIDGET_FG(child, colour, override)
        else:
            gui.trace("In ttk mode - trying to set FG to %s", colour)
            self.ttkStyle.configure("TLabel", foreground=colour)
            self.ttkStyle.configure("TFrame", foreground=colour)

    def getBg(self):
        if self._getContainerProperty('type') == WIDGET_NAMES.RootPage:
            if not self.ttkFlag:
                return self.bgLabel.cget("bg")
            else:
                return self.bgLabel.cget("background")
        else:
            if not self.ttkFlag:
                return self._getContainerProperty('container').cget("bg")
            else:
                return None

    def getFg(self):
        return self._getContainerProperty("fg")

    fg = property(getFg, setFg)

    # self.topLevel = Tk()
    # self.appWindow = CanvasDnd, fills all of self.topLevel
    # self.tb = Frame, at top of appWindow
    # self.container = Frame, at bottom of appWindow => C_ROOT container
    # self.bglabel = Label, filling all of container
    def setBg(self, colour, override=False, tint=False):
        if not self.ttkFlag:
            if self._getContainerProperty('type') == WIDGET_NAMES.RootPage:
# removed this - it makes the screen do funny stuff
#                self.appWindow.config(background=colour)
                self.bgLabel.config(background=colour)

            self._getContainerProperty('container').config(background=colour)

            for child in self._getContainerProperty('container').winfo_children():
                if not self._isWidgetContainer(child):

                    # horrible hack to deal with weird ScrolledText
                    # winfo_children returns ScrolledText as a Frame
                    # therefore can't call some functions
                    # this gets the ScrolledText version
                    if gui.GET_WIDGET_CLASS(child) == "Frame":
                        for val in self.widgetManager.group(WIDGET_NAMES.TextArea).values():
                            if str(val) == str(child):
                                child = val
                                break

                    gui.SET_WIDGET_BG(child, colour, override, tint)
        else:
            gui.trace("In ttk mode - trying to set BG to %s", colour)
            self.ttkStyle.configure(".", background=colour)

    bg = property(getBg, setBg)

    @staticmethod
    def _isWidgetContainer(widget):
        try:
            if widget.isContainer:
                return True
        except:
            pass
        return False

    def setResizable(self, canResize=True):
        self._getTopLevel().isResizable = canResize
        if self._getTopLevel().isResizable:
            self._getTopLevel().resizable(True, True)
        else:
            self._getTopLevel().resizable(False, False)

    def getResizable(self):
        return self._getTopLevel().isResizable

    resizable = property(getResizable, setResizable)

    def _doTitleBar(self):
        if self.platform == self.MAC:
            self.warn("Title bar hiding doesn't work on MAC - app may become unresponsive.")
        elif self.platform == self.LINUX:
            self.warn("Title bar hiding doesn't work on LINUX - app may become unresponsive.")
        self._getTopLevel().overrideredirect(not self.hasTitleBar)

    def hideTitleBar(self):
        self.hasTitleBar = False
        self._doTitleBar()

    def showTitleBar(self):
        self.hasTitleBar = True
        self._doTitleBar()

    # function to set the window's title
    def setTitle(self, title):
        self._getTopLevel().title(title)

    # function to get the window title
    def getTitle(self):
        return self._getTopLevel().title()

    # property for setTitle
    title = property(getTitle, setTitle)

    # set an icon
    def setIcon(self, image):
        container = self._getTopLevel()
        container.winIcon = image
        if image.endswith('.ico'):
            container.wm_iconbitmap(image)
        else:
            icon = self._getImage(image)
            container.iconphoto(True, icon)

    def getIcon(self):
        container = self._getTopLevel()
        return container.winIcon

    # property for setTitle
    icon = property(getIcon, setIcon)

    def _getCanvas(self, param=-1):
        if len(self.containerStack) > 1 and self.containerStack[param]['type'] == WIDGET_NAMES.SubWindow:
            return self.containerStack[param]['container']
        elif len(self.containerStack) > 1:
            return self._getCanvas(param-1)
        else:
            return self.topLevel

    def _getTopLevel(self):
        if len(self.containerStack) > 1 and self._getContainerProperty('type') == WIDGET_NAMES.SubWindow:
            return self._getContainerProperty('container')
        else:
            return self.topLevel

    # make the window transparent (between 0 & 1)
    def setTransparency(self, percentage):
        if self.platform == self.LINUX:
            self.warn("Transparency not supported on LINUX")
        else:
            if percentage > 1:
                percentage = float(percentage) / 100
            self._getTopLevel().attributes("-alpha", percentage)

    def getTransparency(self):
        return self._getTopLevel().attributes("-alpha") * 100

    # property for setTransparency
    transparency = property(getTransparency, setTransparency)

##############################
# functions to deal with tabbing and right clicking
##############################
    def _focusNextWindow(self, event):
        event.widget.tk_focusNext().focus_set()
        nowFocus = self.topLevel.focus_get()
        if isinstance(nowFocus, Entry):
            nowFocus.select_range(0, END)
        return("break")

    def _focusLastWindow(self, event):
        event.widget.tk_focusPrev().focus_set()
        nowFocus = self.topLevel.focus_get()
        if isinstance(nowFocus, Entry):
            nowFocus.select_range(0, END)
        return("break")

    # creates relevant bindings on the widget
    def _addRightClickMenu(self, widget):
        if self.platform in [self.WINDOWS, self.LINUX]:
            widget.bind('<Button-3>', self._rightClick)
        else:
            widget.bind('<Button-2>', self._rightClick)

    def _rightClick(self, event, menu="EDIT"):
        event.widget.focus()
        if menu == "EDIT":
            if self._prepareCopyAndPasteMenu(event):
                self.widgetManager.get(WIDGET_NAMES.Menu, menu).focus_set()
                self.widgetManager.get(WIDGET_NAMES.Menu, menu).post(event.x_root - 10, event.y_root - 10)
        else:
            self.widgetManager.get(WIDGET_NAMES.Menu, menu).focus_set()
            self.widgetManager.get(WIDGET_NAMES.Menu, menu).post(event.x_root - 10, event.y_root - 10)
        return "break"

#####################################
# FUNCTION to configure widgets
#####################################

    def configureAllWidgets(self, kind, option, value):
        items = list(self.widgetManager.group(kind))
        self.configureWidgets(kind, items, option, value)

    def configureWidgets(self, kind, names, option, value):
        if not isinstance(names, list):
            self.configureWidget(kind, names, option, value)
        else:
            for widg in names:
                # incase 2D array, eg. buttons
                if isinstance(widg, list):
                    for widg2 in widg:
                        self.configureWidget(kind, widg2, option, value)
                else:
                    self.configureWidget(kind, widg, option, value)

    def getWidget(self, kind, name, val=None):
        # if val is set (RadioButtons) - append it
        if val is not None: name+= "-" + val
        return self.widgetManager.get(kind, name)

    def getWidgetProperty(self, kind, name, val, prop):
        return self.getWidget(kind, name, val).cget(prop)

    def addWidget(self, title, widg, row=None, column=0, colspan=0, rowspan=0):
        ''' adds a generic widget to the appJar grid manager '''
        self.widgetManager.verify(WIDGET_NAMES.Widget, title)
        self._positionWidget(widg, row, column, colspan, rowspan)
        self.widgetManager.add(WIDGET_NAMES.Widget, title, widg)

    def _getWidgetList(self, kind, name, limit):
        # gets a list of items of this type
        # limit is used to only get a single radio button - for events
        if kind == WIDGET_NAMES.RadioButton:
            items = self.widgetManager.group(kind)
            new_items = []
            for k, v in items.items():
                if k.startswith(name+"-"):
                    new_items.append(v)

            if len(new_items) == 0:
                raise Exception("No RadioButtons found with that name " + name)
            else:
                items = new_items
                # stops multiple events...
                if limit: items = [items[0]]
        else:
            # get the list of items for this type, and validate the widget is in the list
            self.widgetManager.check(kind, name)
            items = self.widgetManager.group(kind)
            items = [items[name]]
        return items

    def configureWidget(self, kind, name, option, value, key=None, deprecated=False):
        gui.trace("Configuring: %s of %s with %s of %s", name, kind, option, value)
        # warn about deprecated functions
        if deprecated:
            self.warn("Deprecated config function (%s) used for %s -> %s use %s deprecated", option, WIDGET_NAMES.name(kind), name, deprecated)

        # will return multiple items if radio button...
        items = self._getWidgetList(kind, name, limit=option in ['change', 'command'])

        # loop through each item, and try to reconfigure it
        # this will often fail - widgets have varied config options
        for item in items:
            try:
                if option == 'background':
                    gui.SET_WIDGET_BG(item, value, True)
                elif option == 'foreground':
                    gui.SET_WIDGET_FG(item, value, True)
                elif option == 'disabledforeground':
                    item.config(disabledforeground=value)
                elif option == 'disabledbackground':
                    item.config(disabledbackground=value)
                elif option == 'activeforeground':
                    item.config(activeforeground=value)
                elif option == 'activebackground':
                    item.config(activebackground=value)
                elif option == 'inactiveforeground':
                    if kind in [WIDGET_NAMES.TabbedFrame, WIDGET_NAMES.Table]:
                        item.config(inactiveforeground=value)
                    else:
                        self.warn("Error configuring %s: can't set inactiveforeground", name )
                elif option == 'inactivebackground':
                    if kind in [WIDGET_NAMES.TabbedFrame, WIDGET_NAMES.Table]:
                        item.config(inactivebackground=value)
                    else:
                        self.warn("Error configuring %s: can't set inactivebackground", name)
                elif option == 'width':
                    item.config(width=value)
                elif option == 'height':
                    item.config(height=value)
                elif option == 'state':
                    # make entries readonly - can still copy/paste
                    but = None
                    if kind == WIDGET_NAMES.Entry:
                        if value == "disabled" and hasattr(item, 'but'):
                            but = item.but
                            item.unbind("<Button-1>")
                            value = "readonly"
                        elif value == 'normal' and hasattr(item, 'but') and item.cget('state') != 'normal':
                            but = item.but
                            item.bind("<Button-1>", item.click_command, "+")

                    if self.ttkFlag:
                        gui.trace("%s configured with ttk state %s", name, value)
                        item.state([value])
                        if but is not None: but.state([value])
                    else:
                        item.config(state=value)
                        if but is not None: but.config(state=value)

                elif option == 'relief':
                    item.config(relief=value)
                elif option == 'style':
                    if self.ttkFlag:
                        gui.trace("%s configured with ttk style %s", name, value)
                        item.config(style=value)
                    else:
                        self.warn("Error configuring %s: can't set ttk style, not in ttk mode.", name)
                elif option in ['align', 'anchor']:
                    if kind == WIDGET_NAMES.Entry or gui.GET_WIDGET_CLASS(item) == 'SelectableLabel':
                        if value == W: value = LEFT
                        elif value == E: value = RIGHT
                        item.config(justify=value)
                    elif kind == WIDGET_NAMES.LabelFrame:
                        item.config(labelanchor=value)
                    else:
                        if value == LEFT: value = "w"
                        elif value == RIGHT: value = "e"
                        item.config(anchor=value)
                elif option == 'cursor':
                    item.config(cursor=value)
                elif option == 'tooltip':
                    self._addTooltip(item, value)
                elif option == 'disableTooltip':
                    self._disableTooltip(item)
                elif option == 'enableTooltip':
                    self._enableTooltip(item)
                elif option == "focus":
                    item.focus_set()
                    if kind == WIDGET_NAMES.Entry:
                        if not self.ttkFlag:
                            item.icursor(END)
                            item.xview(END)
                        else:
                            item.icursor(END)
                            item.xview(len(item.get()))

                # event bindings
                elif option == 'over':
                    self._bindOverEvent(kind, name, item, value, option, key)
                elif option == 'drag':
                    self._bindDragEvent(kind, name, item, value, option, key)
                elif option in ['command', "change", "submit"]:
                    self._bindEvent(kind, name, item, value, option, key)

                elif option == 'sticky':
                    info = {}
                    # need to reposition the widget in its grid
                    if self._widgetHasContainer(kind, item):
                        # pack uses LEFT & RIGHT & BOTH
                        info["side"] = value
                        if value.lower() == "both":
                            info["expand"] = 1
                            info["side"] = "right"
                        else:
                            info["expand"] = 0
                    else:
                        # grid uses E+W
                        if value.lower() == "left":
                            side = W
                        elif value.lower() == "right":
                            side = E
                        elif value.lower() == "both":
                            side = W + E
                        else:
                            side = value.upper()
                        info["sticky"] = side
                    self._repackWidget(item, info)
                elif option == 'padding':
                    if value[1] is None:
                        item.config(padx=value[0][0], pady=value[0][1])
                    else:
                        item.config(padx=value[0], pady=value[1])
                elif option == 'ipadding':
                    if value[1] is None:
                        item.config(ipadx=value[0][0], ipady=value[0][1])
                    else:
                        item.config(ipadx=value[0], ipady=value[1])
                elif option == 'rightClick':
                    self._bindRightClick(item, value)

                elif option == 'internalDrop':
                    self._registerInternalDropTarget(item, value)

                elif option == 'internalDrag':
                    self._registerInternalDragSource(kind, name, item, value)

                elif option == 'externalDrop':
                    self._registerExternalDropTarget(name, item, value[0], value[1])

                elif option == 'externalDrag':
                    self._registerExternalDragSource(name, item, value)

            except TclError as e:
                self.warn("Error configuring %s: %s", name, str(e))

    # generic function for over events
    def _validateFunctionList(self, functions, mode):
        if type(functions) == tuple:
            functions = list(functions)
        elif type(functions) != list:
            functions = [functions]

        if len(functions) == 1:
            functions.append(None)
        if len(functions) != 2:
            raise Exception("Invalid arguments, set<widget> %s Function requires 1 or 2 functions to be passed in.", mode)

        return functions

    def _bindOverEvent(self, kind, name, widget, functions, eventType, key=None):
        functions = self._validateFunctionList(functions, "Over")

        if functions[0] is not None:
            widget.bind("<Enter>", self.MAKE_FUNC(functions[0], name), add="+")
        if functions[1] is not None:
            widget.bind("<Leave>", self.MAKE_FUNC(functions[1], name), add="+")

    # generic function for drag events
    def _bindDragEvent(self, kind, name, widget, functions, eventType, key=None):
        functions = self._validateFunctionList(functions, "Drag")

        if kind == WIDGET_NAMES.Label:
            widget.config(cursor="fleur")

            def getLabel(f):
                # loop through all labels
                items = self.widgetManager.group(kind)
                for key, value in items.items():
                    if self._isMouseInWidget(value):
                        self.MAKE_FUNC(f,key)()
                        return

            if functions[0] is not None:
                widget.bind("<ButtonPress-1>", self.MAKE_FUNC(functions[0], name), add="+")
            if functions[1] is not None:
                widget.bind("<ButtonRelease-1>", self.MAKE_FUNC(getLabel, functions[1]), add="+")
        else:
            self.error("Only able to bind drag events to labels")

    # generic function for change/submit/events
    def _bindEvent(self, kind, name, widget, function, eventType, key=None):
        # this will discard the scale value, as default function
        # can't handle it
        if kind == WIDGET_NAMES.Scale:
            cmd = self.MAKE_FUNC(function, name)
            widget.cmd_id = widget.var.trace('w', cmd)
            widget.cmd = cmd
        elif kind == WIDGET_NAMES.OptionBox:
            if widget.kind == "ticks":
                vals = self.widgetManager.get(WIDGET_NAMES.TickOptionBox, name, group=WidgetManager.VARS)
                for o in vals:
                    cmd = self.MAKE_FUNC(function, name)
                    vals[o].cmd_id = vals[o].trace('w', cmd)
                    vals[o].cmd = cmd
            else:
                cmd = self.MAKE_FUNC(function, name)
                # need to trace the variable??
                widget.cmd_id = widget.var.trace('w', cmd)
                widget.cmd = cmd
        elif kind in [WIDGET_NAMES.Entry, WIDGET_NAMES.FileEntry, WIDGET_NAMES.DirectoryEntry]:
            if eventType == "change":
                # not populated by change/submit
                if key is None:
                    key = name
                cmd = self.MAKE_FUNC(function, key)
                # get Entry variable
                var = self.widgetManager.get(WIDGET_NAMES.Entry, name, group=WidgetManager.VARS)
                var.cmd_id = var.trace('w', cmd)
                var.cmd = cmd
            else:
                # not populated by change/submit
                if key is None:
                    key = name
                sbm = self.MAKE_FUNC(function, key)
                widget.sbm_id = widget.bind('<Return>', sbm)
                widget.sbm = sbm
        elif kind == WIDGET_NAMES.TextArea:
            if eventType == "change":
                # get Entry variable
                cmd = self.MAKE_FUNC(function, name)
                widget.bindChangeEvent(cmd)
        elif kind == WIDGET_NAMES.Button:
            if eventType == "change":
                self.warn("Error configuring %s : can't set a change function", name)
            else:
                widget.config(command=self.MAKE_FUNC(function, name))
                widget.bind('<Return>', self.MAKE_FUNC(function, name))
        # make labels clickable, add a cursor, and change the look
        elif kind == WIDGET_NAMES.Label or kind == WIDGET_NAMES.Image:
            if eventType in ["command", "submit"]:
                if self.platform == self.MAC:
                    widget.config(cursor="pointinghand")
                elif self.platform in [self.WINDOWS, self.LINUX]:
                    widget.config(cursor="hand2")

                cmd = self.MAKE_FUNC(function, name)
                widget.bind("<Button-1>", cmd, add="+")
                widget.cmd = cmd
                # these look good, but break when dialogs take focus
                #up = widget.cget("relief").lower()
                # down="sunken"
                # make it look like it's pressed
                #widget.bind("<Button-1>",lambda e: widget.config(relief=down), add="+")
                #widget.bind("<ButtonRelease-1>",lambda e: widget.config(relief=up))
            elif eventType == "change":
                self.warn("Error configuring %s : can't set a change function", name)
        elif kind == WIDGET_NAMES.ListBox:
            cmd = self.MAKE_FUNC(function, name)
            widget.bind('<<ListboxSelect>>', cmd)
            widget.cmd = cmd
        elif kind in [WIDGET_NAMES.RadioButton]:
            cmd = self.MAKE_FUNC(function, name)
            # get rb variable
            var = self.widgetManager.get(WIDGET_NAMES.RadioButton, name, group=WidgetManager.VARS)

            # only allow one trace to be bound
            # users are more likely to call multiple binds on radios
            # because they all share one var
            if hasattr(var, "cmd_id"):
                var.trace_vdelete('w', var.cmd_id)

            var.cmd_id = var.trace('w', cmd)
            var.cmd = cmd
        elif kind in [WIDGET_NAMES.Properties, WIDGET_NAMES.FrameStack, WIDGET_NAMES.Table]:
            cmd = self.MAKE_FUNC(function, name)
            widget.setChangeFunction(cmd)
        elif kind == WIDGET_NAMES.SpinBox:
            widget.cmd = self.MAKE_FUNC(function, name)
            widget.cmd_id = widget.var.trace("w", widget.cmd)
        elif kind == WIDGET_NAMES.PanedFrame:
            widget.cmd = self.MAKE_FUNC(function, name)
            widget.bind("<Configure>", widget.cmd)
        else:
            if kind not in [WIDGET_NAMES.CheckBox]:
                self.warn("Unmanaged binding of %s to %s", eventType, name)
            cmd = self.MAKE_FUNC(function, name)
            widget.config(command=cmd)
            widget.cmd = cmd

    # dynamic way to create the configuration functions
    def _buildConfigFuncs(self):
        # loop through all the available widgets
        # and make all the below functons for each one
        for v in WIDGET_NAMES.funcs():
            k = WIDGET_NAMES.get(v)
            exec( "def set" + v +
                "Bg(self, name, val): self.configureWidgets(" +
                str(k) + ", name, 'background', val)")
            exec("gui.set" + v + "Bg=set" + v + "Bg")
            exec( "def set" + v +
                "Fg(self, name, val): self.configureWidgets(" +
                str(k) + ", name, 'foreground', val)")
            exec("gui.set" + v + "Fg=set" + v + "Fg")

            exec( "def set" + v +
                "DisabledFg(self, name, val): self.configureWidgets(" +
                str(k) + ", name, 'disabledforeground', val)")
            exec("gui.set" + v + "DisabledFg=set" + v + "DisabledFg")
            exec( "def set" + v +
                "DisabledBg(self, name, val): self.configureWidgets(" +
                str(k) + ", name, 'disabledbackground', val)")
            exec("gui.set" + v + "DisabledBg=set" + v + "DisabledBg")

            exec( "def set" + v +
                "ActiveFg(self, name, val): self.configureWidgets(" +
                str(k) + ", name, 'activeforeground', val)")
            exec("gui.set" + v + "ActiveFg=set" + v + "ActiveFg")
            exec( "def set" + v +
                "ActiveBg(self, name, val): self.configureWidgets(" +
                str(k) + ", name, 'activebackground', val)")
            exec("gui.set" + v + "ActiveBg=set" + v + "ActiveBg")

            exec( "def set" + v +
                "InactiveFg(self, name, val): self.configureWidgets(" +
                str(k) + ", name, 'inactiveforeground', val)")
            exec("gui.set" + v + "InactiveFg=set" + v + "InactiveFg")
            exec( "def set" + v +
                "InactiveBg(self, name, val): self.configureWidgets(" +
                str(k) + ", name, 'inactivebackground', val)")
            exec("gui.set" + v + "InactiveBg=set" + v + "InactiveBg")

            exec( "def set" + v +
                "Width(self, name, val): self.configureWidgets(" +
                str(k) + ", name, 'width', val)")
            exec("gui.set" + v + "Width=set" + v + "Width")
            exec( "def set" + v +
                "Height(self, name, val): self.configureWidgets(" +
                str(k) + ", name, 'height', val)")
            exec("gui.set" + v + "Height=set" + v + "Height")
            exec( "def set" + v +
                "State(self, name, val): self.configureWidgets(" +
                str(k) + ", name, 'state', val)")
            exec("gui.set" + v + "State=set" + v + "State")
            exec( "def set" + v +
                "Padding(self, name, x, y=None): self.configureWidgets(" +
                str(k) + ", name, 'padding', [x, y])")
            exec("gui.set" + v + "Padding=set" + v + "Padding")

            exec( "def set" + v +
                "IPadding(self, name, x, y=None): self.configureWidgets(" +
                str(k) + ", name, 'ipadding', [x, y])")
            exec("gui.set" + v + "IPadding=set" + v + "IPadding")

            exec( "def set" + v +
                "InPadding(self, name, x, y=None): self.configureWidgets(" +
                str(k) + ", name, 'ipadding', [x, y])")
            exec("gui.set" + v + "InPadding=set" + v + "InPadding")

            # drag and drop stuff
            exec( "def set" + v +
                "DropTarget(self, name, function=None, replace=True): self.configureWidgets(" +
                str(k) + ", name, 'externalDrop', [function, replace])")
            exec("gui.set" + v + "DropTarget=set" + v + "DropTarget")

            exec( "def set" + v +
                "DragSource(self, name, function=None): self.configureWidgets(" +
                str(k) + ", name, 'externalDrag', function)")
            exec("gui.set" + v + "DragSource=set" + v + "DragSource")

            exec( "def register" + v +
                "Draggable(self, name, function=None): self.configureWidgets(" +
                str(k) + ", name, 'internalDrag', function)")
            exec("gui.register" + v + "Draggable=register" + v + "Draggable")

            exec( "def register" + v +
                "Droppable(self, name, function=None): self.configureWidgets(" +
                str(k) + ", name, 'internalDrop', function)")
            exec("gui.register" + v + "Droppable=register" + v + "Droppable")

            exec( "def set" + v +
                "Style(self, name, val): self.configureWidget(" +
                str(k) + ", name, 'style', val)")
            exec("gui.set" + v + "Style=set" + v + "Style")

            # might not all be necessary, could make exclusion list
            exec( "def set" + v +
                "Relief(self, name, val): self.configureWidget(" +
                str(k) + ", name, 'relief', val)")
            exec("gui.set" + v + "Relief=set" + v + "Relief")
            exec( "def set" + v +
                "Align(self, name, val): self.configureWidget(" +
                str(k) + ", name, 'align', val)")
            exec("gui.set" + v + "Align=set" + v + "Align")
            exec( "def set" + v +
                "Anchor(self, name, val): self.configureWidget(" +
                str(k) + ", name, 'anchor', val)")
            exec("gui.set" + v + "Anchor=set" + v + "Anchor")

            exec( "def set" + v +
                "Tooltip(self, name, val): self.configureWidget(" +
                str(k) + ", name, 'tooltip', val)")
            exec("gui.set" + v + "Tooltip=set" + v + "Tooltip")

            exec( "def disable" + v +
                "Tooltip(self, name): self.configureWidget(" +
                str(k) + ", name, 'disableTooltip', None)")
            exec("gui.disable" + v + "Tooltip=disable" + v + "Tooltip")

            exec( "def enable" + v +
                "Tooltip(self, name): self.configureWidget(" +
                str(k) + ", name, 'enableTooltip', None)")
            exec("gui.enable" + v + "Tooltip=enable" + v + "Tooltip")

            # function setters
            exec( "def set" + v +
                "ChangeFunction(self, name, val): self.configureWidget(" +
                str(k) + ", name, 'change', val)")
            exec("gui.set" + v + "ChangeFunction=set" + v + "ChangeFunction")
            exec( "def set" + v +
                "SubmitFunction(self, name, val): self.configureWidget(" +
                str(k) + ", name, 'submit', val)")
            exec("gui.set" + v + "SubmitFunction=set" + v + "SubmitFunction")
            exec( "def set" + v +
                "DragFunction(self, name, val): self.configureWidget(" +
                str(k) + ", name, 'drag', val)")
            exec("gui.set" + v + "DragFunction=set" + v + "DragFunction")
            exec( "def set" + v +
                "OverFunction(self, name, val): self.configureWidget(" +
                str(k) + ", name, 'over', val)")
            exec("gui.set" + v + "OverFunction=set" + v + "OverFunction")

            # http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/cursors.html
            exec( "def set" + v +
                "Cursor(self, name, val): self.configureWidget(" +
                str(k) + ", name, 'cursor', val)")
            exec("gui.set" + v + "Cursor=set" + v + "Cursor")
            exec( "def set" + v +
                "Focus(self, name): self.configureWidget(" +
                str(k) + ", name, 'focus', None)")
            exec("gui.set" + v + "Focus=set" + v + "Focus")

            # change the stickyness
            exec( "def set" + v +
                "Sticky(self, name, pos): self.configureWidget(" +
                str(k) + ", name, 'sticky', pos)")
            exec("gui.set" + v + "Sticky=set" + v + "Sticky")

            # add right click
            exec( "def set" + v +
                "RightClick(self, name, menu): self.configureWidget(" +
                str(k) + ", name, 'rightClick', menu)")
            exec("gui.set" + v + "RightClick=set" + v + "RightClick")

            # functions to manage widgets
            exec( "def show" + v +
                "(self, name): self.showWidgetType(" +
                str(k) + ", name)")
            exec("gui.show" + v + "=show" + v)
            exec( "def hide" + v +
                "(self, name, collapse=False): self.hideWidgetType(" +
                str(k) + ", name, collapse)")
            exec("gui.hide" + v + "=hide" + v)
            exec( "def remove" + v +
                "(self, name, collapse=False): self.removeWidgetType(" +
                str(k) + ", name, collapse)")
            exec("gui.remove" + v + "=remove" + v)
            exec( "def move" + v +
                "(self, name, row=None, column=0, colspan=0, rowspan=0, sticky=W+E): self.moveWidgetType(" +
                str(k) + ", name, row, column, colspan, rowspan, sticky)")
            exec("gui.move" + v + "=move" + v)

            exec( "def empty" + v +
                "(self, name): self._emptyContainerType(" +
                str(k) + ", name)")
            exec("gui.empty" + v + "=empty" + v)

            # convenience functions for enable/disable
            # might not all be necessary, could make exclusion list
            exec( "def enable" + v +
                "(self, name): self.configureWidget(" +
                str(k) + ", name, 'state', 'normal')")
            exec("gui.enable" + v + "=enable" + v)
            exec( "def disable" + v +
                "(self, name): self.configureWidget(" +
                str(k) + ", name, 'state', 'disabled')")
            exec("gui.disable" + v + "=disable" + v)

            # group functions
            exec( "def set" + v +
                "Widths(self, names, val): self.configureWidgets(" +
                str(k) + ", names, 'width', val)")
            exec("gui.set" + v + "Widths=set" + v + "Widths")
            exec( "def setAll" + v +
                "Widths(self, val): self.configureAllWidgets(" +
                str(k) + ", 'width', val)")
            exec("gui.setAll" + v + "Widths=setAll" + v + "Widths")

            exec( "def set" + v +
                "Heights(self, names, val): self.configureWidgets(" +
                str(k) + ", names, 'height', val)")
            exec("gui.set" + v + "Heights=set" + v + "Heights")
            exec( "def setAll" + v +
                "Heights(self, val): self.configureAllWidgets(" +
                str(k) + ", 'height', val)")
            exec("gui.setAll" + v + "Heights=setAll" + v + "Heights")

            exec( "def get" + v +
                "Widget(self, name, val=None): return self.getWidget(" +
                str(k) + ", name, val)")
            exec("gui.get" + v + "Widget=get" + v + "Widget")

            exec( "def get" + v +
                "Bg(self, name, val=None): return self.getWidgetProperty(" +
                str(k) + ", name, val, 'bg')")
            exec("gui.get" + v + "Bg=get" + v + "Bg")

#####################################
#  FUNCTION to hide/show/remove widgets
#####################################
    def _widgetHasContainer(self, kind, item):
        if kind in (
            WIDGET_NAMES.Scale,
            WIDGET_NAMES.Entry,
            WIDGET_NAMES.SpinBox,
            WIDGET_NAMES.OptionBox,
            WIDGET_NAMES.Label) and item.inContainer:
            return True
        else:
            return False

    def _cloneWidget(self, widget, parent):
        clone = widget.__class__(parent)
        for key in widget.configure():
            clone.configure({key: widget.cget(key)})

        origProps = widget.__dict__
        for x in origProps:
            if x not in ['_w', '_tclCommands', '_name', 'master', 'tk']:
                setattr(clone, x, origProps[x])

        return clone

    def moveWidgetType(self, kind, name, row=None, column=0, colspan=0, rowspan=0, sticky=W+E):
        self.hideWidgetType(kind, name, collapse=True)
        widget = self.widgetManager.get(kind, name)

        container = self.getContainer()
        if container != widget.master:
            widget = self._cloneWidget(widget, container)
            self.widgetManager.update(kind, name, widget)

        self._positionWidget(widget, row, column, colspan, rowspan, sticky, updateBg=False)
        return widget


    def hideWidgetType(self, kind, name, collapse=False):
        items = self._getWidgetList(kind, name, limit=False)

        for item in items:
            if self._widgetHasContainer(kind, item):
                gui.trace("Hiding widget in container: %s", name)
                widget = item.master
                if hasattr(widget, "inContainer") and widget.inContainer:
                    gui.trace("Have container in container")
                    widget = widget.master
                try: self.widgetManager.get(WIDGET_NAMES.FrameLabel, name).hidden = True
                except: pass
            else:
                gui.trace("Hiding widget: %s", name)
#                if kind in [WIDGET_NAMES.RadioButton]:
#                    for rb in item:
#                        if rb.text == name:
#                            widget = rb
                widget = item

            if "in" in widget.grid_info():
                gui.trace("Widget hidden: %s", name)
                info = widget.grid_info()
                widget.grid_remove()

                if collapse:
                    widget.master.grid_rowconfigure(info["row"], minsize=0, weight=0)
            else:
                gui.trace("Hiding failed - %s not showing", name)

    def showWidgetType(self, kind, name):
        items = self._getWidgetList(kind, name, limit=False)

        for item in items:
            if self._widgetHasContainer(kind, item):
                gui.trace("Showing widget in container: %s", name)
                widget = item.master
                if hasattr(widget, "inContainer") and widget.inContainer:
                    gui.trace("Have container in container")
                    widget = widget.master
                try: self.widgetManager.get(WIDGET_NAMES.FrameLabel, name).hidden = False
                except: pass
            else:
                msg = "Showing widget"
                widget = item

            # only show the widget, if it's not already showing
            if "in" not in widget.grid_info():
                gui.trace("Widget shown: %s", name)
                widget.grid()
    #            self._updateLabelBoxes(name, widget.grid_info()['column'])
            else:
                gui.trace("Showing failed - %s already showing", name)

    def emptySubWindow(self, title):
        # not generated by function generator
        self._emptyContainerType(WIDGET_NAMES.SubWindow, title)

    def emptyCurrentContainer(self):
        cConf = self.containerStack[-1]
        kind = WIDGET_NAMES.name(cConf['type'])
        title = cConf['title']
        gui.trace('Emptying current container %s: %s', kind, title)

        if not self._emptyContainerObj(cConf['container']):
            gui.trace('No widgets found in current container %s: %s to empty', kind, title)

        cConf = self._prepContainer(cConf["title"], cConf["type"], cConf["container"], 0, 1)
        self.containerStack[-1] = cConf

    def _emptyContainerType(self, kind, title):
        kind = WIDGET_NAMES.name(kind)
        gui.trace('Emptying %s: %s', kind, title)
        cName = kind + "__" + title
        try:
            cConf = self.widgetManager.get(WIDGET_NAMES.ContainerLog, cName)
        except KeyError:
            raise Exception("Attempted to empty invalid " + kind + ": " + str(title))

        if not self._emptyContainerObj(cConf['container']):
            gui.trace('No widgets found in %s: %s to empty', kind, title)

    def removeAllWidgets(self, current=False, sub=False):
        if current:
            self.emptyCurrentContainer()
        else:
            gui.trace('Removing all widgets from appJar')

            if sub: self.destroyAllSubWindows()

            containerData = self.containerStack[0]
            container = containerData['container']
            self._emptyContainerObj(container)

            # reset container values
            containerData = self._prepContainer(containerData["title"], containerData["type"], containerData["container"], 0, 1)
            self.containerStack[0] = containerData

#            self.widgetManager.reset(WIDGET_NAMES.keepers)
#            self.setSize(None)

    def _emptyContainerObj(self, container):
        widgs = False
        for child in container.winfo_children():
            self.cleanseWidgets(child)
            widgs = True

        # reset the grid measurements
        for i in range(Grid.grid_size(container)[0]):
            container.columnconfigure(i, minsize=0, weight=0, pad=0)
        for i in range(Grid.grid_size(container)[1]):
            container.rowconfigure(i, minsize=0, weight=0, pad=0)

        return widgs

    def removeWidgetType(self, kind, name, collapse=False):
        if kind == WIDGET_NAMES.RadioButton:
            gui.error("Can't remove widget %s - %s", kind, name)
            return
            
        item = self.widgetManager.get(kind, name)

        # if it's a flasher, remove it
        if item in self.widgetManager.group(WIDGET_NAMES.FlashLabel):
            gui.trace("Remove flash label: %s", name)
            self.widgetManager.remove(WIDGET_NAMES.FlashLabel, item)
            if len(self.widgetManager.group(WIDGET_NAMES.FlashLabel)) == 0:
                self.doFlash = False

        # animated images...

        if self._widgetHasContainer(kind, item):
            gui.trace("Remove widget (%s) in container: %s", kind, name)
            parent = item.master

            # is it a container in a labelBox?
            # if so - remove & destroy the labelBox
            if hasattr(parent, "inContainer") and parent.inContainer:
                gui.trace("Container in container")
                labParent = parent.master

                self.widgetManager.remove(WIDGET_NAMES.FrameBox, labParent)
                self.widgetManager.remove(WIDGET_NAMES.Label, name)
                self.widgetManager.remove(WIDGET_NAMES.FrameLabel, name)
                labParent.grid_forget()
                labParent.destroy()
            # otherwise destroy this container & a label if we have one
            else:
                parent.grid_forget()
                parent.destroy()
                try:
                    self.widgetManager.remove(WIDGET_NAMES.Label, name)
                    self.widgetManager.remove(WIDGET_NAMES.FrameLabel, name)
                except: pass

            self.widgetManager.remove(WIDGET_NAMES.FrameBox, parent)
        else:
            gui.trace("Remove widget: %s", name)
            item.grid_forget()
            self.cleanseWidgets(item)

#####################################
# FUNCTION for managing commands
#####################################
    @staticmethod
    def MAKE_FUNC(funcName, param):
        ''' function to automate lambdas '''
        # make sure we get a function
        if not callable(funcName) and not hasattr(funcName, '__call__'):
            raise Exception("Invalid function: " + str(funcName))

        # check if the function requires arguments
        argsList = getArgs(funcName)
        # if no args, or 1 arg in a bound function
        noArgs = len(argsList[0])==0 or (len(argsList[0])==1 and inspect.ismethod(funcName))

        # if no args/varargs/kwargs then don't give the param
        if noArgs and argsList[1] is None and argsList[2] is None:
            return lambda *args: funcName()
        else:
            return lambda *args: funcName(param)

    def _checkFunc(self, names, funcs):
        singleFunc = None
        if funcs is None:
            return None
        elif callable(funcs):
            singleFunc = funcs
        elif len(names) != len(funcs):
            raise Exception("List sizes don't match")
        return singleFunc

#####################################
# FUNCTIONS to position a widget
#####################################

    # properties for setting container's default rowspan/colspan
    def setColspan(self, colspan):
        self.containerStack[-1]['colspan'] = colspan

    def getColspan(self):
        return self.containerStack[-1]['colspan']

    colspan = property(getColspan, setColspan)

    def setRowspan(self, rowspan):
        self.containerStack[-1]['rowspan'] = rowspan

    def getRowspan(self):
        return self.containerStack[-1]['rowspan']

    rowspan = property(getRowspan, setRowspan)

    def getRow(self):
        return self._getContainerProperty('emptyRow')

    def gr(self):
        return self.getRow()

    def setRow(self, row):
        self.containerStack[-1]['emptyRow'] = row

    row = property(getRow, setRow)

    def _repackWidget(self, widget, params):
        if widget.winfo_manager() == "grid":
            ginfo = widget.grid_info()
            ginfo.update(params)
            widget.grid(ginfo)
        elif widget.winfo_manager() == "pack":
            pinfo = widget.pack_info()
            pinfo.update(params)
            widget.pack(pinfo)
        else:
            raise Exception("Unknown geometry manager: " + widget.winfo_manager())

    # convenience function to set RCS, referencing the current container's
    # settings
    def _getRCS(self, row, column, colspan, rowspan):
        if row in[-1, 'previous', 'p', 'pr']:
            row = self._getContainerProperty('emptyRow') - 1
        else:
            # this is the default,
            if row is None or row in ['next', 'n']:
                row = self._getContainerProperty('emptyRow')
            self.containerStack[-1]['emptyRow'] = row + 1

        if column >= self._getContainerProperty('colCount'):
            self.containerStack[-1]['colCount'] = column + 1
        # if column == 0 and colspan == 0 and self._getContainerProperty('colCount') > 1:
        #      colspan = self._getContainerProperty('colCount')

        return row, column, colspan, rowspan

    @staticmethod
    def GET_WIDGET_CLASS(widget):
        return widget.__class__.__name__

    @staticmethod
    def SET_WIDGET_FG(widget, fg, external=False):
        widgType = gui.GET_WIDGET_CLASS(widget)
        gui.trace("SET_WIDGET_FG: %s - %s", widgType, fg)

        # only configure these widgets if external
        if widgType in ["Link", "Spinbox", "AjText", "AjScrolledText", "Button", "Entry", "AutoCompleteEntry"]:
            if external:
                try: # entry specific settings
                    if not widget.showingDefault:
                        widget.oldFg = fg
                        widget.config(fg=fg)
                    else:
                        widget.oldFg = fg
                except: # other widgets
                    widget.config(fg=fg)
        # handle flash labels
        elif widgType == "Label":
            widget.config(fg=fg)
            widget.origFg=fg
            try: widget.config(bg=widget.origBg)
            except: pass # not a flash label

        elif widgType == "OptionMenu":
            if external:
                widget.config(fg=fg)
                widget["menu"].config(fg=fg)

        # deal with generic groupers
        elif widgType in ["Frame", "LabelFrame", "PanedFrame", "Pane", "ajFrame"]:
            for child in widget.winfo_children():
                gui.SET_WIDGET_FG(child, fg, external)

        # deal with specific containers
        elif widgType == "LabelBox":
            try:
                if not widget.isValidation:
                    gui.SET_WIDGET_FG(widget.theLabel, fg, external)
            except Exception as e:
                gui.SET_WIDGET_FG(widget.theLabel, fg, external)
            gui.SET_WIDGET_FG(widget.theWidget, fg, external)
        elif widgType == "ButtonBox":
            gui.SET_WIDGET_FG(widget.theWidget, fg, external)
            gui.SET_WIDGET_FG(widget.theButton, fg, external)
        elif widgType == "WidgetBox":
            for child in widget.theWidgets:
                gui.SET_WIDGET_FG(child, fg, external)
        elif widgType == "ListBoxContainer":
            if external:
                gui.SET_WIDGET_FG(widget.lb, fg, external)

        # skip these widgets
        elif widgType in ["PieChart", "MicroBitSimulator", "Scrollbar"]:
            pass

        # always try these widgets
        else:
            try:
                widget.config(fg=fg)
            except Exception as e:
                pass

    @staticmethod
    def TINT(widget, colour):
        col = []
        for a, b in enumerate(widget.winfo_rgb(colour)):
            t = int(min(max(0, b / 256 + (255 - b / 256) * .3), 255))
            t = str(hex(t))[2:]
            if len(t) == 1:
                t = '0' + t
            elif len(t) == 0:
                t = '00'
            col.append(t)

        if int(col[0], 16) > 210 and int(col[1], 16) > 210 and int(col[2], 16) > 210:
            if gui.GET_PLATFORM() == gui.LINUX:
                return "#c3c3c3"
            else:
                return "systemHighlight"
        else:
            return "#" + "".join(col)

    # convenience method to set a widget's bg
    @staticmethod
    def SET_WIDGET_BG(widget, bg, external=False, tint=False):

        if bg is None: # ignore empty colours
            return

        widgType = gui.GET_WIDGET_CLASS(widget)
        isDarwin = gui.GET_PLATFORM() == gui.MAC
        isLinux = gui.GET_PLATFORM() == gui.LINUX

        gui.trace("Config %s BG to %s", widgType, bg)

        # these have a highlight border to remove
        hideBorders = [ "Text", "AjText",
            "ScrolledText", "AjScrolledText",
            "Scale", "AjScale",
            "OptionMenu",
            "Entry", "AutoCompleteEntry",
            "Radiobutton", "Checkbutton",
            "Button"]

        # these shouldn't have their BG coloured by default
        noBg = [ "Button",
            "Scale", "AjScale",
            "Spinbox", "Listbox", "OptionMenu",
            "SplitMeter", "DualMeter", "Meter",
            "Entry", "AutoCompleteEntry",
            "Text", "AjText",
            "ScrolledText", "AjScrolledText",
            "ToggleFrame"]

        # remove the highlight borders
        if widgType in hideBorders:
            if widgType == "Entry" and widget.isValidation:
                pass
            elif widgType == "OptionMenu":
                widget["menu"].config(borderwidth=0)
                widget.config(highlightbackground=bg)
                if isDarwin:
                    widget.config(background=bg)
            elif widgType in ["Radiobutton", "Checkbutton"]:
                widget.config(activebackground=bg, highlightbackground=bg)
            else:
                widget.config(highlightbackground=bg)

        # do some fancy tinting
        if external or tint:
            if widgType in ["Button", "Scale", "AjScale"]:
                widget.config(activebackground=gui.TINT(widget, bg))
            elif widgType in ["Entry", "Text", "AjText", "ScrolledText", "AjScrolledText", "AutoCompleteEntry", "Spinbox"]:
                widget.config(selectbackground=gui.TINT(widget, bg))
                widget.config(highlightcolor=gui.TINT(widget, bg))
                if widgType in ["Text", "AjText", "ScrolledText", "AjScrolledText"]:
                    widget.config(inactiveselectbackground=gui.TINT(widget, bg))
                elif widgType == "Spinbox":
                    widget.config(buttonbackground=bg)
            elif widgType == "Listbox":
                widget.config(selectbackground=gui.TINT(widget, bg))
            elif widgType == "OptionMenu":
                widget.config(activebackground=gui.TINT(widget, bg))
                widget["menu"].config(activebackground=gui.TINT(widget, bg))
            elif widgType in ["Radiobutton", "Checkbutton"]:
                widget.config(activebackground=gui.TINT(widget, bg))

        # if this is forced - change everything
        if external:
            widget.config(bg=bg)
            if widgType == "OptionMenu":
                widget["menu"].config(bg=bg)
        # otherwise only colour un-excluded widgets
        elif widgType not in noBg:
            widget.config(bg=bg)

        # deal with flash labels
        if widgType == "Label":
            widget.origBg=bg
            try: widget.config(fg=widget.origFg)
            except: pass # not a flash label

        # now do any of the below containers
        if widgType in ["LabelFrame", "PanedFrame", "Pane", "ajFrame"]:
            for child in widget.winfo_children():
                gui.SET_WIDGET_BG(child, bg, external, tint)
        elif widgType == "LabelBox": # widget with label, in frame
            if widget.theLabel is not None:
                gui.SET_WIDGET_BG(widget.theLabel, bg, external, tint)
            gui.SET_WIDGET_BG(widget.theWidget, bg, external, tint)
        elif widgType == "ButtonBox": # widget with button, in frame
            gui.SET_WIDGET_BG(widget.theWidget, bg, external, tint)
            gui.SET_WIDGET_BG(widget.theButton, bg, external, tint)
        elif widgType == "ListBoxContainer": # list box container
            gui.SET_WIDGET_BG(widget.lb, bg, external, tint)
        elif widgType == "WidgetBox": # group of buttons or labels
            for widg in widget.theWidgets:
                gui.SET_WIDGET_BG(widg, bg, external, tint)

    def _getContainerProperty(self, prop=None):
        if prop is not None:
            return self.containerStack[-1][prop]
        else:
            return self.containerStack[-1]

    def _getContainerBg(self):
        if not self.ttkFlag:
            return self.getContainer()["bg"]
        else:
            return None

    def _getContainerFg(self):
        try:
            return self._getContainerProperty('fg')
        except:
            return "#000000"

    # two important things here:
    # grid - sticky: position of widget in its space (side or fill)
    # row/columns configure - weight: how to grow with GUI
    def _positionWidget( self, widget, row, column=0, colspan=0, rowspan=0, sticky=W+E, updateBg=True):
        # allow item to be added to container
        container = self.getContainer()
        if updateBg and not self.ttkFlag:
            gui.SET_WIDGET_FG(widget, self._getContainerFg())
            gui.SET_WIDGET_BG(widget, self._getContainerBg())

        # alpha paned window placement
        if self._getContainerProperty('type') == WIDGET_NAMES.PanedFrame:
            container.add(widget)
            self.containerStack[-1]['widgets'] = True
            return

        # else, add to grid
        row, column, colspan, rowspan = self._getRCS(row, column, colspan, rowspan)

        # build a dictionary for the named params
        iX = self._getContainerProperty('ipadx')
        iY = self._getContainerProperty('ipady')
        cX = self._getContainerProperty('padx')
        cY = self._getContainerProperty('pady')
        params = {
            "row": row,
            "column": column,
            "ipadx": iX,
            "ipady": iY,
            "padx": cX,
            "pady": cY}

        # sort out rowspan & colspan
        cColspan = self._getContainerProperty("colspan")
        cRowspan = self._getContainerProperty("rowspan")

        if colspan != 0: params["columnspan"] = colspan
        elif cColspan != 0: params["columnspan"] = cColspan

        if rowspan != 0: params["rowspan"] = rowspan
        elif cRowspan != 0: params["rowspan"] = cRowspan

        # 1) if param has sticky, use that
        # 2) if container has sticky - override
        # 3) else, none
        if self._getContainerProperty("sticky") is not None:
            params["sticky"] = self._getContainerProperty("sticky")
        elif sticky is not None:
            params["sticky"] = sticky
        else:
            pass

        # make colspanned widgets expand to fill height of cell
        if rowspan != 0:
            if "sticky" in params:
                if "n" not in params["sticky"]:
                    params["sticky"] += "n"
                if "s" not in params["sticky"]:
                    params["sticky"] += "s"
            else:
                params["sticky"] = "ns"

        # expand that dictionary out as we pass it as a value
        widget.grid(**params)
        self.containerStack[-1]['widgets'] = True
        # if we're in a PANEDFRAME - we need to set parent...
        if self._getContainerProperty('type') == WIDGET_NAMES.Pane:
            self.containerStack[-2]['widgets'] = True

        # configure the row/column to expand equally
        if self._getContainerProperty('expand') in ["ALL", "COLUMN"]:
            Grid.columnconfigure(container, column, weight=1)
        else:
            Grid.columnconfigure(container, column, weight=0)
        if self._getContainerProperty('expand') in ["ALL", "ROW"]:
            Grid.rowconfigure(container, row, weight=1)
        else:
            Grid.rowconfigure(container, row, weight=0)

#        self._getContainerProperty('container').columnconfigure(0, weight=1)
#        self._getContainerProperty('container').rowconfigure(0, weight=1)

#####################################
# FUNCTION to manage containers
#####################################
    # prepares a new empty container dict
    def _prepContainer(self, cTitle, cType, container, row, col, sticky=None):
        containerData = {'type': cType,
                    'title': cTitle,
                    'container': container,
                    'emptyRow': row,
                    'colCount': col,
                    'sticky': sticky,
                    'padx': 0,
                    'pady': 0,
                    'ipadx': 0,
                    'ipady': 0,
                    'expand': "ALL",
                    'widgets': False,
                    'inputFont': self._inputFont,
                    'labelFont': self._labelFont,
                    'buttonFont': self._buttonFont,
                    "fg": self._getContainerFg(),
                    "colspan":0,
                    "rowspan":0,
                    }
        return containerData

    # adds the container to the container stack - makes this the current working container
    def _addContainer(self, cTitle, cType, container, row, col, sticky=None):
        containerData = self._prepContainer(cTitle, cType, container, row, col, sticky)
        self.containerStack.append(containerData)

    def openFrameStack(self, title):
        return self._openContainer(WIDGET_NAMES.FrameStack, title)

    def openSubFrame(self, frameTitle, frameNumber):
        return self._openContainer(WIDGET_NAMES.SubFrame, frameTitle+"__"+str(frameNumber))

    def openRootPage(self, title):
        return self._openContainer(WIDGET_NAMES.RootPage, title)

    def openLabelFrame(self, title):
        return self._openContainer(WIDGET_NAMES.LabelFrame, title)

    def openFrame(self, title):
        try: return self._openContainer(WIDGET_NAMES.Frame, title)
        except: return self._openContainer(WIDGET_NAMES.SubFrame, title)

    def openToggleFrame(self, title):
        return self._openContainer(WIDGET_NAMES.ToggleFrame, title)

    def openPagedWindow(self, title):
        return self._openContainer(WIDGET_NAMES.PagedWindow, title)

    def openPage(self, windowTitle, pageNumber):
        return self._openContainer(WIDGET_NAMES.Page, windowTitle+"__"+str(pageNumber))

    def openTabbedFrame(self, title):
        return self._openContainer(WIDGET_NAMES.TabbedFrame, title)

    def openTab(self, frameTitle, tabTitle):
        return self._openContainer(WIDGET_NAMES.Tab, frameTitle+"__"+tabTitle)

    def openNotebook(self, title):
        return self._openContainer(WIDGET_NAMES.Notebook, title)

    def openNote(self, frameTitle, tabTitle):
        return self._openContainer(WIDGET_NAMES.Notebook, frameTitle+"__"+tabTitle)

    def openPanedFrame(self, title):
        return self._openContainer(WIDGET_NAMES.PanedFrame, title)

    def openPane(self, title):
        return self._openContainer(WIDGET_NAMES.Pane, title)

    def openSubWindow(self, title):
        return self._openContainer(WIDGET_NAMES.SubWindow, title)

    def openScrollPane(self, title):
        return self._openContainer(WIDGET_NAMES.ScrollPane, title)

    # function to reload the specified container
    def _openContainer(self, kind, title):
        # get the cached container config for this container
        kind = WIDGET_NAMES.name(kind)
        cName = kind + "__" + title
        try:
            cConf = self.widgetManager.get(WIDGET_NAMES.ContainerLog, cName)
        except KeyError:
            raise Exception("Attempted to open invalid " + kind + ": " + str(title))

        self.containerStack.append(cConf)
        return cConf['container']

    # returns the current working container
    def getContainer(self):
        container = self._getContainerProperty('container')
        if self._getContainerProperty('type') == WIDGET_NAMES.ScrollPane:
            return container.interior
        elif self._getContainerProperty('type') == WIDGET_NAMES.PagedWindow:
            return container.getPage()
        elif self._getContainerProperty('type') == WIDGET_NAMES.ToggleFrame:
            return container.getContainer()
        elif self._getContainerProperty('type') == WIDGET_NAMES.SubWindow:
            return container.canvasPane
        else:
            return container

    # if possible, removes the current container
    def _removeContainer(self):
        if len(self.containerStack) == 1:
            raise Exception("Can't remove container, already in root window.")
        else:
            container = self.containerStack.pop()
            if not container['widgets']:
                self.warn("Closing empty container: %s", container['title'])

            # store the container so that it can be re-opened later
            name = WIDGET_NAMES.name(container["type"]) + "__" + container["title"]
            try:
                self.widgetManager.add(WIDGET_NAMES.ContainerLog, name, container)
            except:
                pass # we'll ignore, as that means we already added it...
            return container

    # functions to start the various containers
    def startContainer(self, fType, title, row=None, column=0, colspan=0, rowspan=0, sticky=None, name=None):
        if name is None: name = title
        if fType == WIDGET_NAMES.LabelFrame:
            # first, make a LabelFrame, and position it correctly
            self.widgetManager.verify(WIDGET_NAMES.LabelFrame, title)
            if not self.ttkFlag:
                container = LabelFrame(self.getContainer(), text=name, relief="groove")
                container.config(background=self._getContainerBg(), font=self._getContainerProperty('labelFont'))
            else:
                container = ttk.LabelFrame(self.getContainer(), text=name, relief="groove")

            container.DEFAULT_TEXT = name
            container.isContainer = True
            self.setPadX(5)
            self.setPadY(5)
            self._positionWidget(container, row, column, colspan, rowspan, "nsew")
            self.widgetManager.add(WIDGET_NAMES.LabelFrame, title, container)

            # now, add to top of stack
            self._addContainer(title, WIDGET_NAMES.LabelFrame, container, 0, 1, sticky)
            return container
        elif fType == WIDGET_NAMES.Canvas:
            # first, make a canvas, and position it correctly
            self.widgetManager.verify(WIDGET_NAMES.Canvas, title)
            container = Canvas(self.getContainer())
            container.isContainer = True
            self._positionWidget(container, row, column, colspan, rowspan, "nsew")
            self.widgetManager.add(WIDGET_NAMES.Canvas, title, container)

            # now, add to top of stack
            self._addContainer(title, WIDGET_NAMES.Canvas, container, 0, 1, "")
            return container
        elif fType == WIDGET_NAMES.TabbedFrame:
            self.widgetManager.verify(WIDGET_NAMES.TabbedFrame, title)
            tabbedFrame = self._tabbedFrameMaker(self.getContainer(), self.ttkFlag, font=self._getContainerProperty('labelFont'))
            if not self.ttkFlag:
                tabbedFrame.config(bg=self._getContainerBg())
#            tabbedFrame.isContainer = True
            self._positionWidget(
                tabbedFrame,
                row,
                column,
                colspan,
                rowspan,
                sticky=sticky)
            self.widgetManager.add(WIDGET_NAMES.TabbedFrame, title, tabbedFrame)

            # now, add to top of stack
            self._addContainer(title, WIDGET_NAMES.TabbedFrame, tabbedFrame, 0, 1, sticky)
            return tabbedFrame
        elif fType == WIDGET_NAMES.Tab:
            # add to top of stack
            self.containerStack[-1]['widgets'] = True
            tabTitle = self._getContainerProperty('title') + "__" + title
            tab = self._getContainerProperty('container').addTab(title)
            self._addContainer(tabTitle, WIDGET_NAMES.Tab, tab, 0, 1, sticky)
            return tab
        elif fType == WIDGET_NAMES.Notebook:
            if not self.ttkFlag:
                raise Exception("Cannot create a ttk Notebook, unless ttk is enabled.")
            self.widgetManager.verify(WIDGET_NAMES.Notebook, title)
            notebook = ttk.Notebook(self.getContainer())
#            tabbedFrame.isContainer = True
            self._positionWidget(
                notebook,
                row,
                column,
                colspan,
                rowspan,
                sticky=sticky)
            self.widgetManager.add(WIDGET_NAMES.Notebook, title, notebook)

            # now, add to top of stack
            self._addContainer(title, WIDGET_NAMES.Notebook, notebook, 0, 1, sticky)
            return notebook
        elif fType == WIDGET_NAMES.Note:
            # add to top of stack
            self.containerStack[-1]['widgets'] = True
            noteTitle = self._getContainerProperty('title') + "__" + title
            frame = ttk.Frame(self._getContainerProperty('container'))
            self._getContainerProperty('container').add(frame, text=title)
            self._addContainer(noteTitle, WIDGET_NAMES.Note, frame, 0, 1, sticky)
            return frame
        elif fType == WIDGET_NAMES.PanedFrame:
            # if we previously put a frame for widgets
            # remove it
            if self._getContainerProperty('type') == WIDGET_NAMES.Pane:
                self.stopContainer()

            # now, add the new pane
            self.widgetManager.verify(WIDGET_NAMES.PanedFrame, title)
            pane = PanedWindow(
                self.getContainer(),
                showhandle=True,
                sashrelief="groove",
                bg=self._getContainerBg())

            pane.isContainer = True
            self._positionWidget(
                pane, row, column, colspan, rowspan, sticky=sticky)
            self.widgetManager.add(WIDGET_NAMES.PanedFrame, title, pane)

            # now, add to top of stack
            self._addContainer(title, WIDGET_NAMES.PanedFrame, pane, 0, 1, sticky)

            # now, add a frame to the pane
            self.startContainer(WIDGET_NAMES.Pane, title)
            return pane
        elif fType == WIDGET_NAMES.Pane:
            # create a frame, and add it to the pane
            pane = Pane(self.getContainer(), bg=self._getContainerBg())
            pane.isContainer = True
            self._getContainerProperty('container').add(pane)
            self.widgetManager.add(WIDGET_NAMES.Pane, title, pane)

            # now, add to top of stack
            self._addContainer(title, WIDGET_NAMES.Pane, pane, 0, 1, sticky)
            return pane
        elif fType == WIDGET_NAMES.ScrollPane:
            self.widgetManager.verify(WIDGET_NAMES.ScrollPane, title)
            # naned used to diabled sctollbars
            if name not in ["horizontal", "vertical", ""]:
                gui.warn("ScrollPane %s: Invalid value for disabled, must be one of 'horizontal' or 'vertical'", title)
            scrollPane = ScrollPane(self.getContainer(), disabled=name)
            if not self.ttkFlag:
                scrollPane.config(bg=self._getContainerBg())
            scrollPane.isContainer = True
            self._positionWidget(
                scrollPane,
                row,
                column,
                colspan,
                rowspan,
                sticky=sticky)
            self.widgetManager.add(WIDGET_NAMES.ScrollPane, title, scrollPane)

            # now, add to top of stack
            self._addContainer(title, WIDGET_NAMES.ScrollPane, scrollPane, 0, 1, sticky)
            return scrollPane
        elif fType == WIDGET_NAMES.ToggleFrame:
            self.widgetManager.verify(WIDGET_NAMES.ToggleFrame, title)
            toggleFrame = ToggleFrame(self.getContainer(), title=title, bg=self._getContainerBg())
            toggleFrame.configure(font=self._getContainerProperty('labelFont'))
            toggleFrame.isContainer = True
            self._positionWidget(
                toggleFrame,
                row,
                column,
                colspan,
                rowspan,
                sticky=sticky)
            self._addContainer(title, WIDGET_NAMES.ToggleFrame, toggleFrame, 0, 1, "nw")
            self.widgetManager.add(WIDGET_NAMES.ToggleFrame, title, toggleFrame)
            return toggleFrame
        elif fType == WIDGET_NAMES.PagedWindow:
            # create the paged window
            pagedWindow = PagedWindow(self.getContainer(), title=title, bg=self._getContainerBg(), width=200, height=400, buttonFont=self._getContainerProperty('buttonFont'), titleFont=self._getContainerProperty('labelFont'))
            # bind events
            self.topLevel.bind("<Left>", pagedWindow.showPrev)
            self.topLevel.bind("<Control-Left>", pagedWindow.showFirst)
            self.topLevel.bind("<Right>", pagedWindow.showNext)
            self.topLevel.bind("<Control-Right>", pagedWindow.showLast)
            # register it as a container
            pagedWindow.isContainer = True
            self._positionWidget(pagedWindow, row, column, colspan, rowspan, sticky=sticky)
            self._addContainer(title, WIDGET_NAMES.PagedWindow, pagedWindow, 0, 1, "nw")
            self.widgetManager.add(WIDGET_NAMES.PagedWindow, title, pagedWindow)
            return pagedWindow
        elif fType == WIDGET_NAMES.Page:
            page = self._getContainerProperty('container').addPage()
            page.isContainer = True
            self._addContainer(title, WIDGET_NAMES.Page, page, 0, 1, sticky)
            self.containerStack[-1]['expand'] = "None"
            return page
        elif fType == WIDGET_NAMES.FrameStack:
            # create the paged window
            frameStack = FrameStack(self.getContainer(), bg=self._getContainerBg())
            self.widgetManager.add(WIDGET_NAMES.FrameStack, title, frameStack)
            # register it as a container
            frameStack.isContainer = True
            self._positionWidget(frameStack, row, column, colspan, rowspan, sticky=sticky)
            self._addContainer(title, WIDGET_NAMES.FrameStack, frameStack, 0, 1, "news")
            return frameStack
        elif fType == WIDGET_NAMES.Frame:
            # first, make a Frame, and position it correctly
            self.widgetManager.verify(WIDGET_NAMES.Frame, title)
            container = self._makeAjFrame()(self.getContainer())
            container.isContainer = True
            container.config(background=self._getContainerBg())
            self._positionWidget( container, row, column, colspan, rowspan, "nsew")
            self.widgetManager.add(WIDGET_NAMES.Frame, title, container)

            # now, add to top of stack
            self._addContainer(title, WIDGET_NAMES.Frame, container, 0, 1, sticky)
            return container
        elif fType == WIDGET_NAMES.SubFrame:
            subFrame = self._getContainerProperty('container').addFrame()
            subFrame.isContainer = True
            self._addContainer(title, WIDGET_NAMES.SubFrame, subFrame, 0, 1, "news")
            self.widgetManager.add(WIDGET_NAMES.Frame, title, subFrame)
            return subFrame
        else:
            raise Exception("Unknown container: " + fType)

#####################################
# Notebooks
#####################################

    @contextmanager
    def notebook(self, title, row=None, column=0, colspan=0, rowspan=0, sticky="NSEW", **kwargs):
        try:
            note = self.startNotebook(title, row, column, colspan, rowspan, sticky)
        except ItemLookupError:
            note = self.openNotebook(title)
        self.configure(**kwargs)
        try: yield note
        finally: self.stopNotebook()

    def startNotebook(self, title, row=None, column=0, colspan=0, rowspan=0, sticky="NSEW"):
        return self.startContainer(WIDGET_NAMES.Notebook, title, row, column, colspan, rowspan, sticky)

    def stopNotebook(self):
        # auto close the existing TAB - keep it?
        if self._getContainerProperty('type') == WIDGET_NAMES.Note:
            self.warn("You didn't STOP the previous NOTE")
            self.stopContainer()
        self.stopContainer()

    @contextmanager
    def note(self, title, tabTitle=None, **kwargs):
        if tabTitle is None:
            note = self.startNote(title)
        else:
            note = self.openNote(title, tabTitle)
        self.configure(**kwargs)
        try: yield note
        finally: self.stopNote()

    def startNote(self, title):
        # auto close the previous TAB - keep it?
        if self._getContainerProperty('type') == WIDGET_NAMES.Note:
            self.warn("You didn't STOP the previous NOTE")
            self.stopContainer()
        elif self._getContainerProperty('type') != WIDGET_NAMES.Notebook:
            raise Exception(
                "Can't add a Note to the current container: ", self._getContainerProperty('type'))
        return self.startContainer(WIDGET_NAMES.Note, title)

    def stopNote(self):
        if self._getContainerProperty('type') != WIDGET_NAMES.Note:
            raise Exception("Can't stop a NOTE, currently in:",
                            self._getContainerProperty('type'))
        self.stopContainer()
    """
    def startCanvas(self, title, row=None, column=0, colspan=0, rowspan=0, sticky="news"):
        return self.startContainer(WIDGET_NAMES.Canvas, title)

    def stopCanvas(self):
        if self._getContainerProperty('type') != WIDGET_NAMES.Canvas:
            raise Exception("Can't stop a CANVAS, currently in:", self._getContainerProperty('type'))
        self.stopContainer()

    @contextmanager
    def canvas(self, title, row=None, column=0, colspan=0, rowspan=0, sticky="NSEW"):
        try:
            canvas = self.startCanvas(title, row, column, colspan, rowspan, sticky)
        except ItemLookupError:
            canvas = self.openCanvas(title)
        try: yield canvas
        finally: self.stopCanvas()
    """

#####################################
# Tabbed Frames
#####################################

    #################################
    # TabbedFrame Class
    #################################
    def _tabbedFrameMaker(self, master, useTtk=False, **kwargs):
        global OrderedDict
        if OrderedDict is None:
            from collections import OrderedDict

        class TabBorder(Frame, object):
            def __init__(self, master, **kwargs):
                super(TabBorder, self).__init__(master, **kwargs)
                self.config(borderwidth=0, highlightthickness=0, bg='darkGray')

        class TabContainer(frameBase, object):
            def __init__(self, master, **kwargs):
                super(TabContainer, self).__init__(master, **kwargs)
                TabBorder(self, height=2).pack(side=TOP, expand=True, fill=X)
                TabBorder(self, width=2).pack(side=LEFT, fill=Y, expand=0)

        class TabText(labelBase, object):
            def __init__(self, master, func, text, **kwargs):
                super(TabText, self).__init__(master, text=text, **kwargs)
                self.disabled = False
                self.DEFAULT_TEXT = text
                self.hidden = False
                self.bind("<Button-1>", lambda *args: func(text))
                self.border = TabBorder(master, width=2)

            def rename(self, newName):
                # use the DEFAULT_TEXT if necessary
                if newName is None: newName = self.DEFAULT_TEXT
                self.config(text=newName)

            def hide(self):
                self.hidden = True
                self.border.pack_forget()
                self.pack_forget()

            def display(self, fill=False, beforeTab=None, afterTab=None):
                self.border.pack_forget()
                self.pack_forget()
                if not self.hidden:
                    if fill: self.pack(side=LEFT, ipady=4, ipadx=4, expand=True, fill=BOTH, before=beforeTab, after=afterTab)
                    else: self.pack(side=LEFT, ipady=4, ipadx=4, before=beforeTab, after=afterTab)
                    self.border.pack(side=LEFT, fill=Y, expand=0, before=beforeTab, after=afterTab)

        class TabbedFrame(frameBase, object):
            def __init__(self, master, fill=False, changeOnFocus=True, font=None, **kwargs):
                # main frame & tabContainer inherit BG colour
                super(TabbedFrame, self).__init__(master, **kwargs)
                self.fill = fill
                self.selectedTab = None
                self.changeOnFocus = changeOnFocus
                self.changeEvent = None
                self.beforeTab = None
                self.afterTab = None

                # layout the grid
                Grid.columnconfigure(self, 0, weight=1)
                Grid.rowconfigure(self, 1, weight=1)

                # create two containers
                self.tabContainer = TabContainer(self, **kwargs)
                self.panes = FrameStack(self)
                self.panes.SKIP_CLEANSE = True

                # now grid minimised or stretched
                if self.fill: self.tabContainer.grid(row=0, sticky=W + E)
                else: self.tabContainer.grid(row=0, sticky=W)
                self.panes.grid(row=1, sticky="NESW")
                self.EMPTY_PANE = self.panes.addFrame()

                # nain store dictionary: name = [tab, pane]
                self.widgetStore = OrderedDict()

                # looks
                self.tabFont = font
                if gui.GET_PLATFORM() == gui.MAC: self.inactiveCursor="pointinghand"
                elif gui.GET_PLATFORM() in [gui.WINDOWS, gui.LINUX]: self.inactiveCursor="hand2"
                # selected tab & all panes
                self.activeFg = "#000000"
                self.activeBg = "#F6F6F6"
                # other tabs
                self.inactiveFg = "#000000"
                self.inactiveBg = "#DADADA"
                # disabled tabs
                self.disabledFg = "gray"
                self.disabledBg = "darkGray"

                if useTtk:
                    self.ttkStyle = ttk.Style()
                    self.ttkStyle.configure("ActiveTab.TLabel", foreground=self.activeFg, background=self.activeBg)
                    self.ttkStyle.configure("InactiveTab.TLabel", foreground=self.inactiveFg, background=self.inactiveBg)
                    self.ttkStyle.configure("DisabledTab.TLabel", foreground=self.disabledFg, background=self.disabledBg)
                    self.ttkStyle.configure("DisabledTab.TFrame", background=self.disabledBg)
                    self.EMPTY_PANE.config(style="DisabledTab.TFrame")
                else:
                    self.EMPTY_PANE.config(bg=self.disabledBg)

            def config(self, cnf=None, **kw):
                self.configure(cnf, **kw)

            def setBeforeTab(self, tab=None):
                if tab is not None:
                    self.beforeTab = self.widgetStore[tab][0]
                else:
                    self.beforeTab = None

            def setAfterTab(self, tab=None):
                if tab is not None:
                    self.afterTab = self.widgetStore[tab][0]
                else:
                    self.afterTab = None

            def configure(self, cnf=None, **kw):
                kw = gui.CLEAN_CONFIG_DICTIONARY(**kw)
                if "activeforeground" in kw: self.activeFg = kw.pop("activeforeground")
                if "activebackground" in kw: self.activeBg = kw.pop("activebackground")
                if "fg" in kw: self.inactiveFg = kw.pop("fg")
                if "inactivebackground" in kw: self.inactiveBg = kw.pop("inactivebackground")
                if "inactiveforeground" in kw: self.inactiveFg = kw.pop("inactiveforeground")
                if "disabledforeground" in kw: self.disabledFg = kw.pop("disabledforeground")
                if "disabledbackground" in kw: self.disabledBg = kw.pop("disabledbackground")
                if "bg" in kw: self.tabContainer.configure(bg=kw["bg"])
                if "font" in kw: self.tabFont.config(kw.pop("font"))
                if "command" in kw: self.changeEvent = kw.pop("command")

                # just in case
                if not useTtk:
                    self.EMPTY_PANE.config(bg=self.disabledBg)
                else:
                    self.ttkStyle.configure("ActiveTab.TLabel", foreground=self.activeFg, background=self.activeBg)
                    self.ttkStyle.configure("InactiveTab.TLabel", foreground=self.inactiveFg, background=self.inactiveBg)
                    self.ttkStyle.configure("DisabledTab.TLabel", foreground=self.disabledFg, background=self.disabledBg)
                    self.ttkStyle.configure("DisabledTab.TFrame", background=self.disabledBg)

                # update tabs if we have any
                self._configTabs()

                # propagate any left over confs
                super(TabbedFrame, self).config(cnf, **kw)

            def hideTab(self, title):
                if title not in self.widgetStore.keys(): raise ItemLookupError("Invalid tab name: " + title)
                self.widgetStore[title][0].hide()
                if self.selectedTab == title:
                    self.selectedTab = None
                    self._findNewTab()
                self._configTabs()

            def deleteTab(self, title):
                self.hideTab(title)

                tab = self.widgetStore[title][0]
                tab.border.destroy()
                tab.destroy()

                pane = self.widgetStore[title][1]
                pane.grid_forget()
                pane.destroy()

                del self.widgetStore[title]

            def showTab(self, title):
                if title not in self.widgetStore.keys(): raise ItemLookupError("Invalid tab name: " + title)
                self.widgetStore[title][0].hidden = False
                self.expandTabs(self.fill)
                if self.selectedTab == None:
                    self.changeTab(title)

            def disableAllTabs(self, disabled=True):
                for tab in self.widgetStore.keys():
                    self.disableTab(tab, disabled, refresh=False)
                self._configTabs()
                if disabled:
                    self.selectedTab = None
                    self.EMPTY_PANE.lift()

            def disableTab(self, tabName, disabled=True, refresh=True):
                if tabName not in self.widgetStore.keys(): raise ItemLookupError("Invalid tab name: " + tabName)

                tab = self.widgetStore[tabName][0]
                tab.disabled = disabled

                if not disabled and not tab.hidden and self.selectedTab is None:
                    self.selectedTab = tabName
                elif disabled and self.selectedTab == tabName:
                    self.selectedTab = None
                    if refresh: self._findNewTab()

                if refresh:
                    self._configTabs()

            def addTab(self, text, **kwargs):
                # check for duplicates
                if text in self.widgetStore: raise ItemLookupError("Duplicate tabName: " + text)

                tab = TabText(self.tabContainer, text=text, func=self.changeTab, font=self.tabFont, **kwargs)
                tab.display(self.fill, beforeTab=self.beforeTab, afterTab=self.afterTab)

                # create the pane
                pane = self.panes.addFrame()
                if not useTtk:
                    pane.config(bg=self.activeBg)

                # log the first tab as the selected tab
                if self.selectedTab is None:
                    self.selectedTab = text

                # log the widgets
                self.widgetStore[text] = [tab, pane]
                self._configTabs()

                return pane

            def getTab(self, title):
                if title not in self.widgetStore.keys(): raise ItemLookupError("Invalid tab name: " + title)
                else: return self.widgetStore[title][1]

            def expandTabs(self, fill=True):
                self.fill = fill

                # update the tabConatiner
                self.tabContainer.grid_forget()
                if self.fill: self.tabContainer.grid(row=0, sticky=W + E)
                else: self.tabContainer.grid(row=0, sticky=W)

                for key in list(self.widgetStore.keys()):
                    tab = self.widgetStore[key][0]
                    tab.display(self.fill)

            def renameTab(self, tabName, newName=None):
                if tabName not in self.widgetStore.keys():
                    raise ItemLookupError("Invalid tab name: " + tabName)

                self.widgetStore[tabName][0].rename(newName)

            def changeTab(self, tabName, callFunction=True):
                if tabName not in self.widgetStore.keys(): raise ItemLookupError("Invalid tab name: " + tabName)
                # stop if already selected or disabled
                if self.selectedTab == tabName or self.widgetStore[tabName][0].disabled or self.widgetStore[tabName][0].hidden:
                    return

                self.selectedTab = tabName
                self._configTabs()
                if self.changeEvent is not None and callFunction: self.changeEvent(tabName)

            def getSelectedTab(self):
                return self.selectedTab

            def setFont(self, **kwargs):
                self.tabFont.config(**kwargs)

            def _findNewTab(self):
                for key in list(self.widgetStore.keys()):
                    if not self.widgetStore[key][0].disabled and not self.widgetStore[key][0].hidden:
                        self.changeTab(key)
                        return

                # if we're here - all tabs are disabled
                self.selectedTab = None
                self.EMPTY_PANE.lift()

            def _configTabs(self):
                for key in list(self.widgetStore.keys()):
                    if self.widgetStore[key][0].disabled:
                        if not useTtk:
                            self.widgetStore[key][0].config(bg=self.disabledBg, fg=self.disabledFg, cursor="")
                        else:
                            self.widgetStore[key][0].config(style="DisabledTab.TLabel", cursor="")
                    else:
                        if key == self.selectedTab:
                            if not useTtk:
                                self.widgetStore[key][0].config(bg=self.widgetStore[key][1].cget('bg'), fg=self.activeFg, cursor="")
                            else:
                                self.widgetStore[key][0].config(style="SelectedTab.TLabel", cursor="")
                            self.widgetStore[key][1].lift()
                        else:
                            if not useTtk:
                                self.widgetStore[key][0].config(bg=self.inactiveBg, fg=self.inactiveFg, cursor=self.inactiveCursor)
                            else:
                                self.widgetStore[key][0].config(style="InactiveTab.TLabel", cursor=self.inactiveCursor)

        return TabbedFrame(master, **kwargs)

    @contextmanager
    def tabbedFrame(self, title, row=None, column=0, colspan=0, rowspan=0, sticky="NSEW", **kwargs):
        try:
            tabs = self.startTabbedFrame(title, row, column, colspan, rowspan, sticky)
        except ItemLookupError:
            tabs = self.openTabbedFrame(title)
        command = kwargs.pop("change", None)
        if command is not None: self.setTabbedFrameChangeCommand(title, command)
        self.configure(**kwargs)
        try: yield tabs
        finally: self.stopTabbedFrame()

    def startTabbedFrame(self, title, row=None, column=0, colspan=0, rowspan=0, sticky="NSEW"):
        return self.startContainer(WIDGET_NAMES.TabbedFrame, title, row, column, colspan, rowspan, sticky)

    def stopTabbedFrame(self):
        # auto close the existing TAB - keep it?
        if self._getContainerProperty('type') == WIDGET_NAMES.Tab:
            self.warn("You didn't STOP the previous TAB")
            self.stopContainer()
        self.stopContainer()

    def setTabbedFrameChangeCommand(self, title, func):
        nb = self.widgetManager.get(WIDGET_NAMES.TabbedFrame, title)
        command = self.MAKE_FUNC(func, title)
        nb.config(command=command)

    def setTabbedFrameTabExpand(self, title, expand=True):
        nb = self.widgetManager.get(WIDGET_NAMES.TabbedFrame, title)
        nb.expandTabs(expand)

    def setTabbedFrameSelectedTab(self, title, tab, callFunction=True):
        nb = self.widgetManager.get(WIDGET_NAMES.TabbedFrame, title)
        try:
            nb.changeTab(tab, callFunction)
        except KeyError:
            raise ItemLookupError("Invalid tab name: " + str(tab))

    def setTabbedFrameDisabledTab(self, title, tab, disabled=True):
        nb = self.widgetManager.get(WIDGET_NAMES.TabbedFrame, title)
        nb.disableTab(tab, disabled)

    def setTabbedFrameDisableAllTabs(self, title, disabled=True):
        nb = self.widgetManager.get(WIDGET_NAMES.TabbedFrame, title)
        nb.disableAllTabs(disabled)

    def deleteTabbedFrameTab(self, title, tab):
        nb = self.widgetManager.get(WIDGET_NAMES.TabbedFrame, title)
        self.cleanseWidgets(nb.getTab(tab))
        nb.deleteTab(tab)

    def showTabbedFrameTab(self, title, tab):
        nb = self.widgetManager.get(WIDGET_NAMES.TabbedFrame, title)
        nb.showTab(tab)

    def hideTabbedFrameTab(self, title, tab):
        nb = self.widgetManager.get(WIDGET_NAMES.TabbedFrame, title)
        nb.hideTab(tab)

    def setTabText(self, title, tab, newText=None):
        nb = self.widgetManager.get(WIDGET_NAMES.TabbedFrame, title)
        nb.renameTab(tab, newText)

    def setTabFont(self, title, **kwargs):
        nb = self.widgetManager.get(WIDGET_NAMES.TabbedFrame, title)
        nb.setFont(**kwargs)

    def setTabBg(self, title, tab, colour):
        nb = self.widgetManager.get(WIDGET_NAMES.TabbedFrame, title)
        tab = nb.getTab(tab)
        gui.SET_WIDGET_BG(tab, colour)
        # tab.config(bg=colour)
        #gui.SET_WIDGET_BG(tab, colour)
        for child in tab.winfo_children():
            gui.SET_WIDGET_BG(child, colour)

    @contextmanager
    def tab(self, title, tabTitle=None, **kwargs):
        beforeTab = kwargs.pop("beforeTab", None)
        afterTab = kwargs.pop("afterTab", None)
        if tabTitle is None:
            try:
                tab = self.startTab(title, beforeTab, afterTab)
            except ItemLookupError:
                if self._getContainerProperty('type') != WIDGET_NAMES.TabbedFrame:
                    raise Exception("Can't open a Tab in the current container: ", self._getContainerProperty('type'))
                else:
                    tabTitle = self._getContainerProperty('title')
                    tab = self.openTab(tabTitle, title)
        else:
            tab = self.openTab(title, tabTitle)
        self.configure(**kwargs)
        try: yield tab
        finally: self.stopTab()

    def startTab(self, title, beforeTab=None, afterTab=None):

        if beforeTab is not None and afterTab is not None:
            self.warn("You can't specify a before and after value for tab: %s", title)
            beforeTab = afterTab = None

        # auto close the previous TAB - keep it?
        if self._getContainerProperty('type') == WIDGET_NAMES.Tab:
            self.warn("You didn't STOP the previous TAB")
            self.stopContainer()
        elif self._getContainerProperty('type') != WIDGET_NAMES.TabbedFrame:
            raise Exception("Can't add a Tab to the current container: ", self._getContainerProperty('type'))

        tf = self.widgetManager.get(WIDGET_NAMES.TabbedFrame, self._getContainerProperty("title"))
        tf.setBeforeTab(beforeTab)
        tf.setAfterTab(afterTab)
        tab = self.startContainer(WIDGET_NAMES.Tab, title)
        tf.setBeforeTab()
        tf.setAfterTab()
        return tab

    def getTabbedFrameSelectedTab(self, title):
        nb = self.widgetManager.get(WIDGET_NAMES.TabbedFrame, title)
        return nb.getSelectedTab()

    def stopTab(self):
        if self._getContainerProperty('type') != WIDGET_NAMES.Tab:
            raise Exception("Can't stop a TAB, currently in:",
                            self._getContainerProperty('type'))
        self.stopContainer()

#####################################
# Simple Tables
#####################################

    def _getDbTables(self, db):
        ''' query the specified database, and get a list of table names '''
        self._importSqlite3()
        if not sqlite3:
            self.error("Unable to load DB tables - can't load sqlite3")
            return []

        query = "SELECT DISTINCT tbl_name FROM sqlite_master ORDER BY tbl_name COLLATE NOCASE"
        data = []

        with sqlite3.connect(db) as conn:
            cursor = conn.cursor()
            cursor.execute(query)
            for row in cursor:
                data.append(row[0])
        return data

    def replaceDbTable(self, title, db, table):
        grid = self.widgetManager.get(WIDGET_NAMES.Table, title)
        grid.db = db
        grid.dbTable = table
        self._importSqlite3()
        if not sqlite3:
            self.error("Unable to load DB data - can't load sqlite3")
            return

        with sqlite3.connect(db) as conn:
            cursor = conn.cursor()
            dataQuery = 'SELECT * from ' + table

            # select all data
            cursor.execute(dataQuery)
            self.setTableHeaders(title, cursor)
            self.replaceAllTableRows(title, cursor)
        self.topLevel.update_idletasks()

    def disableTableEntry(self, title, entryPos, disabled=True):
        grid = self.widgetManager.get(WIDGET_NAMES.Table, title)
        grid.disableEntry(entryPos, disabled=disabled)

    def refreshDbTable(self, title):
        grid = self.widgetManager.get(WIDGET_NAMES.Table, title)
        self._importSqlite3()
        if not sqlite3:
            self.error("Unable to load DB data - can't load sqlite3")
            return

        with sqlite3.connect(grid.db) as conn:
            cursor = conn.cursor()
            dataQuery = 'SELECT * from ' + grid.dbTable

            # select all data
            cursor.execute(dataQuery)
            self.replaceAllTableRows(title, cursor)

    def refreshDbOptionBox(self, title, selected=None):
        opt = self.widgetManager.get(WIDGET_NAMES.OptionBox, title)
        data = self._getDbTables(opt.db)
        self.changeOptionBox(title, data)
        if selected is not None:
            self.setOptionBox(title, selected)

    def table(self, title, value=None, *args, **kwargs):
        """ simpleGUI - adds, sets & gets tables all in one go """
        widgKind = WIDGET_NAMES.Table
        kind = kwargs.pop("kind", 'normal')

        action=kwargs.pop('action', None)
        addRow=kwargs.pop('addRow', None)
        actionHeading=kwargs.pop('actionHeading', "Action")
        actionButton=kwargs.pop('actionButton', "Press")
        addButton=kwargs.pop('addButton', "Add")
        showMenu=kwargs.pop('showMenu', False)
        horiz=kwargs.pop('horizontal', True)
        change=kwargs.pop('change', None)
        edit=kwargs.pop('edit', None)

        try: self.widgetManager.verify(widgKind, title)
        except: # widget exists
            if value is not None: self.replaceAllTableRows(title, value)
            table = self.getTableEntries(title)
        else: # new widget
            kwargs = self._parsePos(kwargs.pop("pos", []), kwargs)
            if kind == 'normal':
                table = self.addTable(title, value, *args,
                            action=action, addRow=addRow, actionHeading=actionHeading, actionButton=actionButton,
                            addButton=addButton, showMenu=showMenu, horizontal=horiz, **kwargs
                        )
            else:
                table = self.addDbTable(title, value, *args,
                            action=action, addRow=addRow, actionHeading=actionHeading, actionButton=actionButton,
                            addButton=addButton, showMenu=showMenu, horizontal=horiz, **kwargs
                        )
        if change is not None: self.setTableChangeFunction(title, change)
        if edit is not None: self.setTableEditFunction(title, edit)

        if len(kwargs) > 0:
            self._configWidget(title, widgKind, **kwargs)
        return table

    def addDbTable(self, title, value, table, row=None, column=0, colspan=0, rowspan=0,
                action=None, addRow=None, actionHeading="Action", actionButton="Press",
                addButton="Add", showMenu=False, border="solid", **kwargs):
        ''' creates a new Table, displaying the specified database & table '''

        horiz=kwargs.pop('horizontal', True)

        self._importSqlite3()
        if not sqlite3:
            self.error("Unable to load DB data - can't load sqlite3")
            return

        with sqlite3.connect(value) as conn:
            cursor = conn.cursor()
            dataQuery = 'SELECT * from ' + table

            # select all data
            cursor.execute(dataQuery)

            grid = self.addTable(title, cursor, row, column, colspan, rowspan,
                        action, addRow, actionHeading, actionButton,
                        addButton, showMenu, border=border, horizontal=horiz
                    )
        grid.db = value
        grid.dbTable = table
        return grid

    def addTable(self, title, data, row=None, column=0, colspan=0, rowspan=0, action=None, addRow=None,
                actionHeading="Action", actionButton="Press", addButton="Add", showMenu=False, border="solid", **kwargs):
        ''' creates a new table, displaying the specified data '''
        self.widgetManager.verify(WIDGET_NAMES.Table, title)
        wrap=kwargs.pop('wrap', 250)
        horiz=kwargs.pop('horizontal', True)
        if not self.ttkFlag:
            grid = SimpleTable(self.getContainer(), title, data,
                        action, addRow,
                        actionHeading, actionButton, addButton,
                        showMenu, buttonFont=self._getContainerProperty('buttonFont'),
                        font=self.tableFont, background=self._getContainerBg(),
                        queueFunction=self.queueFunction, border=border, wrap=wrap, horizontal=horiz
                    )
        else:
            grid = SimpleTable(self.getContainer(), title, data,
                        action, addRow,
                        actionHeading, actionButton, addButton,
                        showMenu, buttonFont=self._getContainerProperty('buttonFont'),
                        queueFunction=self.queueFunction, border=border, wrap=wrap, horizontal=horiz
                    )
        self._positionWidget(grid, row, column, colspan, rowspan, N+E+S+W)
        self.widgetManager.add(WIDGET_NAMES.Table, title, grid)
        return grid

    def getTableEntries(self, title):
        return self.widgetManager.get(WIDGET_NAMES.Table, title).getEntries()

    def getTableLastChange(self, title):
        return self.widgetManager.get(WIDGET_NAMES.Table, title).getLastChange()

    def getTableSelectedCells(self, title):
        return self.widgetManager.get(WIDGET_NAMES.Table, title).getSelectedCells()

    def selectTableRow(self, title, row, highlight=None):
        grid = self.widgetManager.get(WIDGET_NAMES.Table, title)
        grid.selectRow(row, highlight)

    def setTableEditFunction(self, title, func):
        grid = self.widgetManager.get(WIDGET_NAMES.Table, title)
        cmd = self.MAKE_FUNC(func, title)
        grid.config(edit=cmd)

    def selectTableColumn(self, title, col, highlight=None):
        grid = self.widgetManager.get(WIDGET_NAMES.Table, title)
        grid.selectColumn(col, highlight)

    def addTableRow(self, title, data):
        ''' adds a new row of data to the specified table '''
        grid = self.widgetManager.get(WIDGET_NAMES.Table, title)
        grid.addRow(data)

    def addTableRows(self, title, data):
        ''' adds multiple rows of data to the specified table '''
        grid = self.widgetManager.get(WIDGET_NAMES.Table, title)
        grid.addRows(data, scroll=True)

    def addTableColumn(self, title, columnNumber, data):
        ''' adds a new column of data, in the specified position, to the specified table '''
        grid = self.widgetManager.get(WIDGET_NAMES.Table, title)
        grid.addColumn(columnNumber, data)

    def deleteTableColumn(self, title, columnNumber):
        ''' deletes the specified column from the specified table '''
        grid = self.widgetManager.get(WIDGET_NAMES.Table, title)
        grid.deleteColumn(columnNumber)

    def setTableHeaders(self, title, data):
        ''' change the headers in the specified table '''
        grid = self.widgetManager.get(WIDGET_NAMES.Table, title)
        grid.setHeaders(data)

    def deleteTableRow(self, title, rowNum):
        grid = self.widgetManager.get(WIDGET_NAMES.Table, title)
        grid.deleteRow(rowNum)

    def deleteAllTableRows(self, title):
        grid = self.widgetManager.get(WIDGET_NAMES.Table, title)
        grid.deleteAllRows()

    def sortTable(self, title, columnNumber, descending=False):
        grid = self.widgetManager.get(WIDGET_NAMES.Table, title)
        grid.sort(columnNumber, descending)

    def getTableRowCount(self, title):
        grid = self.widgetManager.get(WIDGET_NAMES.Table, title)
        return grid.getRowCount()

    def getTableRow(self, title, rowNumber):
        grid = self.widgetManager.get(WIDGET_NAMES.Table, title)
        return grid.getRow(rowNumber)

    def confTable(self, title, field, value):
        grid = self.widgetManager.get(WIDGET_NAMES.Table, title)
        kw = {field:value}
        grid.config(**kw)

    def replaceTableRow(self, title, rowNum, data):
        grid = self.widgetManager.get(WIDGET_NAMES.Table, title)
        grid.replaceRow(rowNum, data)

    def replaceAllTableRows(self, title, data, deleteHeader=True):
        grid = self.widgetManager.get(WIDGET_NAMES.Table, title)
        grid.deleteAllRows(deleteHeader=deleteHeader)
        grid.addRows(data, scroll=False)

    # temporary deprecated functions
    def addGrid(self, title, data, row=None, column=0, colspan=0, rowspan=0, action=None, addRow=None,
                actionHeading="Action", actionButton="Press", addButton="Add", showMenu=False):
        ''' DEPRECATED - adds a new grid widget with the specified data '''
        gui.warn("Deprecated - grids renamed to tables")
        return self.addTable(title, data, row, column, colspan, rowspan, action, addRow, actionHeading, actionButton, addButton, showMenu)
    def addDbGrid(self, title, db, table, row=None, column=0, colspan=0, rowspan=0, action=None, addRow=None,
                actionHeading="Action", actionButton="Press", addButton="Add", showMenu=False):
        ''' DEPRECATED - adds a new table widget, with the specified database and table '''
        gui.warn("Deprecated - grids renamed to tables")
        return self.addDbTable(title, db, table, row, column, colspan, rowspan, action, addRow, actionHeading, actionButton, addButton, showMenu)
    def replaceDbGrid(self, title, db, table):
        gui.warn("Deprecated - grids renamed to tables")
        return self.replaceDbTable(title, db, table)
    def refreshDbGrid(self, title):
        gui.warn("Deprecated - grids renamed to tables")
        return self.refreshDbTable(title)
    def selectGridRow(self, title, row, highlight=None):
        gui.warn("Deprecated - grids renamed to tables")
        return self.selectTableRow(title, row, highlight)
    def getGridEntries(self, title):
        gui.warn("Deprecated - grids renamed to tables")
        return self.getTableEntries(title)
    def getGridSelectedCells(self, title):
        gui.warn("Deprecated - grids renamed to tables")
        return self.getTableSelectedCells(title)
    def selectGridColumn(self, title, col, highlight=None):
        return self.selectTableColumn(title, col, highlight)
    def addGridRow(self, title, data):
        ''' DEPRECATED - adds a row of data to the specified grid '''
        return self.addTableRow(title, data)
    def addGridRows(self, title, data):
        ''' DEPRECATED - adds new rows of data to the specified grid '''
        return self.addTableRows(title, data)
    def addGridColumn(self, title, columnNumber, data):
        ''' DEPRECATED - adds a column of data to the specified grid '''
        return self.addTableColumn(title, columnNumber, data)
    def deleteGridColumn(self, title, columnNumber):
        return self.deleteTableColumn(title, columnNumber)
    def setGridHeaders(self, title, data):
        return self.setTableHeaders(title, data)
    def deleteGridRow(self, title, rowNum):
        return self.deleteTableRow(title, rowNum)
    def deleteAllGridRows(self, title):
        return self.deleteAllTableRows(title)
    def sortGrid(self, title, columnNumber, descending=False):
        return self.sortTable(title, columnNumber, descending)
    def getGridRowCount(self, title):
        return self.getTableRowCount(title)
    def getGridRow(self, title, rowNumber):
        return self.getTableRow(title, rowNumber)
    def confGrid(self, title, field, value):
        return self.confTable(title, field, value)
    def replaceGridRow(self, title, rowNum, data):
        return self.replaceTableRow(title, rowNum, data)
    def replaceAllGridRows(self, title, data):
        return self.replaceAllTableRows(title, data)

#####################################
# Paned Frames
#####################################

    @contextmanager
    def panedFrame(self, title, row=None, column=0, colspan=0, rowspan=0, sticky="NSEW", **kwargs):
        vertical = kwargs.pop("vertical", False)
        sash = kwargs.pop("sash", 50)
        reOpen = False
        try:
            pane = self.startPanedFrame(title, row, column, colspan, rowspan, sticky)
        except ItemLookupError:
            reOpen = True
            pane = self.openPane(title)
        if vertical: self.setPanedFrameVertical(title)
        self.configure(**kwargs)
        try: yield pane
        finally:
            if reOpen:
                self.stopContainer()
            else:
                self.stopPanedFrame()
                self.setPaneSashPosition(sash, pane)

    @contextmanager
    def panedFrameVertical(self, title, row=None, column=0, colspan=0, rowspan=0, sticky="NSEW", **kwargs):
        gui.warn('Setting panedFrameVertical(%s) is deprecated, please use panedFrame(vertical=True)', title)
        reOpen = False
        sash = kwargs.pop("sash", 50)
        try:
            pane = self.startPanedFrameVertical(title, row, column, colspan, rowspan, sticky)
        except ItemLookupError:
            reOpen = True
            pane = self.openPane(title)
        self.configure(**kwargs)
        try: yield pane
        finally:
            if reOpen:
                self.stopContainer()
            else:
                self.stopPanedFrame()
                self.setPaneSashPosition(sash, pane)

    def startPanedFrame(self, title, row=None, column=0, colspan=0, rowspan=0, sticky="NSEW"):
        p = self.startContainer(WIDGET_NAMES.PanedFrame, title, row, column, colspan, rowspan, sticky)
        return p

    def startPanedFrameVertical( self, title, row=None, column=0, colspan=0, rowspan=0, sticky="NSEW"):
        p = self.startPanedFrame(title, row, column, colspan, rowspan, sticky)
        self.setPanedFrameVertical(title)
        return p

    def stopPanedFrame(self):
        if self._getContainerProperty('type') == WIDGET_NAMES.Pane:
            self.stopContainer()
        if self._getContainerProperty('type') != WIDGET_NAMES.PanedFrame:
            raise Exception("Can't stop a PANEDFRAME, currently in:",
                            self._getContainerProperty('type'))
        self.stopContainer()

    # make a PanedFrame align vertically
    def setPanedFrameVertical(self, window):
        pane = self.widgetManager.get(WIDGET_NAMES.PanedFrame, window)
        pane.config(orient=VERTICAL)

    def setPaneSashPosition(self, pos, pane=None):
        # convert to a percentage if needed
        if pos > 1: pos = pos / 100.0

        if pane is None:
            if self._getContainerProperty('type') == WIDGET_NAMES.PanedFrame:
                pane = self._getContainerProperty('container')
            elif self.containerStack[-2]['type'] == WIDGET_NAMES.PanedFrame:
                pane = self.containerStack[-2]['container']
            elif self._getContainerProperty('type') == WIDGET_NAMES.Pane:
                pane = self._getContainerProperty('container').parent
            else:
                gui.error("Unable to set sash position - can't find a pane")
                return
        elif type(pane) == str:
            pane = self.widgetManager.get(WIDGET_NAMES.PanedFrame, pane)

        if pane.cget('orient') == 'horizontal':
            w = int(pane.winfo_width() * pos)
            try:
                pane.sash_place(0, w, 0)
                gui.trace('Set horizontal pane: %s to position: %s', pane, pos)
            except TclError as e:
                # no sash to configure - last pane
                pass
        else:
            h = int(pane.winfo_height() * pos)
            try:
                pane.sash_place(0, 0, h)
                gui.trace('Set vertical pane: %s to position: %s', pane, pos)
            except TclError as e:
                # no sash to configure - last pane
                pass

#####################################
# Label Frames
#####################################

    @contextmanager
    def labelFrame(self, title, row=None, column=0, colspan=0, rowspan=0, sticky=W, hideTitle=False, **kwargs):
        name = kwargs.pop("label", kwargs.pop("name", None))
        labelFg = kwargs.pop("labelFg", self.fg)
        try:
            lf = self.startLabelFrame(title, row, column, colspan, rowspan, sticky, hideTitle, name)
        except ItemLookupError:
            lf = self.openLabelFrame(title)
        self.configure(**kwargs)
        if not self.ttkFlag:
            lf.config(fg=labelFg)
        try: yield lf
        finally: self.stopLabelFrame()

    # sticky is alignment inside frame
    # frame will be added as other widgets
    def startLabelFrame(self, title, row=None, column=0, colspan=0, rowspan=0, sticky=W, hideTitle=False, label=None, name=None):
        if label is not None: name = label
        if hideTitle: name = ''
        lf = self.startContainer(WIDGET_NAMES.LabelFrame, title, row, column, colspan, rowspan, sticky, name)
        return lf

    def stopLabelFrame(self):
        if self._getContainerProperty('type') != WIDGET_NAMES.LabelFrame:
            raise Exception("Can't stop a LABELFRAME, currently in:",
                            self._getContainerProperty('type'))
        self.stopContainer()

    # function to set position of title for label frame
    def setLabelFrameTitle(self, title, newTitle):
        frame = self.widgetManager.get(WIDGET_NAMES.LabelFrame, title)
        frame.config(text=newTitle)

#####################################
# Toggle Frames
#####################################

    @contextmanager
    def toggleFrame(self, title, row=None, column=0, colspan=0, rowspan=0, **kwargs):
        try:
            tog = self.startToggleFrame(title, row, column, colspan, rowspan)
        except ItemLookupError:
            tog = self.openToggleFrame(title)
        self.configure(**kwargs)
        try: yield tog
        finally: self.stopToggleFrame()

    ###### TOGGLE FRAMES #######
    def startToggleFrame(self, title, row=None, column=0, colspan=0, rowspan=0):
        return self.startContainer(WIDGET_NAMES.ToggleFrame, title, row, column, colspan, rowspan, sticky="new")

    def stopToggleFrame(self):
        if self._getContainerProperty('type') != WIDGET_NAMES.ToggleFrame:
            raise Exception("Can't stop a TOGGLEFRAME, currently in:",
                            self._getContainerProperty('type'))
        self._getContainerProperty('container').stop()
        self.stopContainer()

    def toggleToggleFrame(self, title):
        toggle = self.widgetManager.get(WIDGET_NAMES.ToggleFrame, title)
        toggle.toggle()

    def setToggleFrameText(self, title, newText):
        toggle = self.widgetManager.get(WIDGET_NAMES.ToggleFrame, title)
        toggle.config(text=newText)

    def getToggleFrameState(self, title):
        toggle = self.widgetManager.get(WIDGET_NAMES.ToggleFrame, title)
        return toggle.isShowing()

#####################################
# Paged Windows
#####################################

    @contextmanager
    def pagedWindow(self, title, row=None, column=0, colspan=0, rowspan=0, **kwargs):
        try:
            pw = self.startPagedWindow(title, row, column, colspan, rowspan)
        except ItemLookupError:
            pw = self.openPagedWindow(title)
        self.configure(**kwargs)
        try: yield pw
        finally: self.stopPagedWindow()

    ###### PAGED WINDOWS #######
    def startPagedWindow(self, title, row=None, column=0, colspan=0, rowspan=0):
        return self.startContainer( WIDGET_NAMES.PagedWindow, title, row, column, colspan, rowspan, sticky="nsew")

    def setPagedWindowPage(self, title, page):
        pager = self.widgetManager.get(WIDGET_NAMES.PagedWindow, title)
        pager.showPage(page)

    def setPagedWindowButtonsTop(self, title, top=True):
        pager = self.widgetManager.get(WIDGET_NAMES.PagedWindow, title)
        pager.setNavPositionTop(top)

    def setPagedWindowButtons(self, title, buttons):
        pager = self.widgetManager.get(WIDGET_NAMES.PagedWindow, title)
        if not isinstance(buttons, list) or len(buttons) != 2:
            raise Exception(
                "You must provide a list of two strings for setPagedWinowButtons()")
        pager.setPrevButton(buttons[0])
        pager.setNextButton(buttons[1])

    def setPagedWindowFunction(self, title, func):
        pager = self.widgetManager.get(WIDGET_NAMES.PagedWindow, title)
        command = self.MAKE_FUNC(func, title)
        pager.registerPageChangeEvent(command)

    def getPagedWindowPageNumber(self, title):
        pager = self.widgetManager.get(WIDGET_NAMES.PagedWindow, title)
        return pager.getPageNumber()

    def showPagedWindowPageNumber(self, title, show=True):
        pager = self.widgetManager.get(WIDGET_NAMES.PagedWindow, title)
        pager.showPageNumber(show)

    def showPagedWindowTitle(self, title, show=True):
        pager = self.widgetManager.get(WIDGET_NAMES.PagedWindow, title)
        pager.showTitle(show)

    def setPagedWindowTitle(self, title, pageTitle):
        pager = self.widgetManager.get(WIDGET_NAMES.PagedWindow, title)
        pager.setTitle(pageTitle)

    @contextmanager
    def page(self, windowTitle=None, pageNumber=None, sticky="nw", **kwargs):
        if windowTitle is None:
            pg = self.startPage(sticky)
        else:
            pg = self.openPage(windowTitle, pageNumber)
        self.configure(**kwargs)
        try: yield pg
        finally: self.stopPage()


    def startPage(self, sticky="nw"):
        if self._getContainerProperty('type') == WIDGET_NAMES.Page:
            self.warn("You didn't STOP the previous PAGE")
            self.stopPage()
        elif self._getContainerProperty('type') != WIDGET_NAMES.PagedWindow:
            raise Exception("Can't start a PAGE, currently in:",
                            self._getContainerProperty('type'))

        self.containerStack[-1]['widgets'] = True

        # generate a page title
        pageNum = self._getContainerProperty('container').frameStack.getNumFrames() + 1
        pageTitle = self._getContainerProperty('title') + "__" + str(pageNum)

        return self.startContainer(WIDGET_NAMES.Page, pageTitle, row=None, column=None, colspan=None, rowspan=None, sticky=sticky)

    def stopPage(self):
        if self._getContainerProperty('type') == WIDGET_NAMES.Page:
            self.stopContainer()
        else:
            raise Exception("Can't stop PAGE, currently in:",
                            self._getContainerProperty('type'))

    def stopPagedWindow(self):
        if self._getContainerProperty('type') == WIDGET_NAMES.Page:
            self.warn("You didn't STOP the previous PAGE")
            self.stopPage()

        if self._getContainerProperty('type') != WIDGET_NAMES.PagedWindow:
            raise Exception("Can't stop a PAGEDWINDOW, currently in:",
                            self._getContainerProperty('type'))

        self._getContainerProperty('container').stopPagedWindow()
        self.stopContainer()

#####################################
# Scrolled Panes
#####################################

    @contextmanager
    def scrollPane(self, title, row=None, column=0, colspan=0, rowspan=0, sticky="NSEW", **kwargs):
        disabled = kwargs.pop("disabled", "")
        try:
            sp = self.startScrollPane(title, row, column, colspan, rowspan, sticky, disabled)
        except ItemLookupError:
            sp = self.openScrollPane(title)
        self.configure(**kwargs)
        try: yield sp
        finally: self.stopScrollPane()


    def startScrollPane(self, title, row=None, column=0, colspan=0, rowspan=0, sticky="NSEW", disabled=""):
        return self.startContainer(WIDGET_NAMES.ScrollPane, title, row, column, colspan, rowspan, sticky, disabled)

    # functions to stop the various containers
    def stopContainer(self): self._removeContainer()

    def stopScrollPane(self):
        if self._getContainerProperty('type') != WIDGET_NAMES.ScrollPane:
            raise Exception("Can't stop a SCROLLPANE, currently in:",
                            self._getContainerProperty('type'))
        self.stopContainer()

    def stopAllPanedFrames(self):
        while True:
            try:
                self.stopPanedFrame()
            except:
                break

#####################################
# Frames
#####################################

    @contextmanager
    def frame(self, title=None, row=None, column=0, colspan=0, rowspan=0, sticky="NSEW", **kwargs):
        if title is None: # new subFrame
            fr = self.startFrame(title, row, column, colspan, rowspan, sticky)
        else:
            frameNumber = kwargs.pop('frameNumber', None)
            try:
                if frameNumber is not None: fr = self.openSubFrame(title, frameNumber)
                else: fr = self.openFrame(title)
            except: # no widget
                fr = self.startFrame(title, row, column, colspan, rowspan, sticky)
        self.configure(**kwargs)
        try: yield fr
        finally: self.stopFrame()

    def startFrame(self, title=None, row=None, column=0, colspan=0, rowspan=0, sticky="NSEW"):
        frameType = WIDGET_NAMES.Frame
        if self._getContainerProperty('type') == WIDGET_NAMES.FrameStack:
            # generate a frame title
            frameNum = self._getContainerProperty('container').getNumFrames()
            title = self._getContainerProperty('title') + "__" + str(frameNum)
            gui.trace("Adding new subFrame: %s", title)

            self.containerStack[-1]['widgets'] = True
            frameType = WIDGET_NAMES.SubFrame
        else:
            if title is None:
                raise Exception("All frames must have a title")
            gui.trace("Adding new frame: %s", title)

        return self.startContainer(frameType, title, row, column, colspan, rowspan, sticky)

    def stopFrame(self):
        if self._getContainerProperty('type') not in [WIDGET_NAMES.Frame, WIDGET_NAMES.SubFrame]:
            raise Exception("Can't stop a FRAME, currently in:",
                            self._getContainerProperty('type'))
        self.stopContainer()

    def raiseFrame(self, title):
        ''' will bring the named frame in front of any others '''
        gui.trace("Raising frame: %s", title)
        self.widgetManager.get(WIDGET_NAMES.Frame, title).lift()

#####################################
# FrameStack
#####################################

    @contextmanager
    def frameStack(self, title, row=None, column=0, colspan=0, rowspan=0, sticky="NSEW", **kwargs):
        change = kwargs.pop("change", None)
        start = kwargs.pop("start", -1)
        try:
            fr = self.startFrameStack(title, row, column, colspan, rowspan, sticky, change=change, start=start)
        except ItemLookupError:
            fr = self.openFrameStack(title)
        self.configure(**kwargs)
        try: yield fr
        finally:
            self.stopFrameStack()

    def startFrameStack(self, title, row=None, column=0, colspan=0, rowspan=0, sticky="news", change=None, start=-1):
        fs = self.startContainer(WIDGET_NAMES.FrameStack, title, row, column, colspan, rowspan, sticky)
        fs.setChangeFunction(change)
        fs.setStartFrame(start)
        return fs

    def stopFrameStack(self):
        if self._getContainerProperty('type') != WIDGET_NAMES.FrameStack:
            raise Exception("Can't stop a FRAMESTACK, currently in:",
                            self._getContainerProperty('type'))
        self.stopContainer()

    def setStartFrame(self, title, num):
        self.widgetManager.get(WIDGET_NAMES.FrameStack, title).setStartFrame(num)

    def nextFrame(self, title, callFunction=True):
        self.widgetManager.get(WIDGET_NAMES.FrameStack, title).showNextFrame(callFunction)
    def prevFrame(self, title, callFunction=True):
        self.widgetManager.get(WIDGET_NAMES.FrameStack, title).showPrevFrame(callFunction)
    def firstFrame(self, title, callFunction=True):
        self.widgetManager.get(WIDGET_NAMES.FrameStack, title).showFirstFrame(callFunction)
    def lastFrame(self, title, callFunction=True):
        self.widgetManager.get(WIDGET_NAMES.FrameStack, title).showLastFrame(callFunction)
    def selectFrame(self, title, num, callFunction=True):
        if type(num) in (list, tuple): num = num[0]
        num = int(num)
        self.widgetManager.get(WIDGET_NAMES.FrameStack, title).showFrame(num, callFunction)

    def countFrames(self, title):
        return self.widgetManager.get(WIDGET_NAMES.FrameStack, title).getNumFrames()
    def getCurrentFrame(self, title):
        return self.widgetManager.get(WIDGET_NAMES.FrameStack, title).getCurrentFrame()
    def getPreviousFrame(self, title):
        return self.widgetManager.get(WIDGET_NAMES.FrameStack, title).getPreviousFrame()

    def frameStackAtStart(self, title):
        return self.widgetManager.get(WIDGET_NAMES.FrameStack, title).atStart()
    def frameStackAtEnd(self, title):
        return self.widgetManager.get(WIDGET_NAMES.FrameStack, title).atEnd()

#####################################
# SubWindows
#####################################

    @contextmanager
    def subWindow(self, name, title=None, modal=False, blocking=False, transient=False, grouped=True, **kwargs):
        visible = kwargs.pop("visible", None)
        try:
            sw = self.startSubWindow(name, title, modal, blocking, transient, grouped)
        except ItemLookupError:
            sw = self.openSubWindow(name)
        self.configure(**kwargs)

        try:
            yield sw
        finally:
            self.stopSubWindow()
        if visible is True: self.showSubWindow(name)

    def startSubWindow(self, name, title=None, modal=False, blocking=False, transient=False, grouped=True):
        self.widgetManager.verify(WIDGET_NAMES.SubWindow, name)
        gui.trace("Starting subWindow %s", name)

        top = SubWindow(self, self.topLevel, name, title=title, stopFunc = self.confirmHideSubWindow,
                        modal=modal, blocking=blocking, transient=transient, grouped=grouped)

        ico = self._getTopLevel().winIcon

        self.widgetManager.add(WIDGET_NAMES.SubWindow, name, top)

        # now, add to top of stack
        self._addContainer(name, WIDGET_NAMES.SubWindow, top, 0, 1, "")

        # add an icon if required
        if ico is not None:
            self.setIcon(ico)
        else:
            top.winIcon = None

        return top

    def stopSubWindow(self):
        container = self.containerStack[-1]
        if container['type'] == WIDGET_NAMES.SubWindow:
            if not hasattr(container["container"], 'ms'):
                self.setMinSize(container["container"])
            self.stopContainer()
        else:
            raise Exception("Can't stop a SUBWINDOW, currently in:",
                            self._getContainerProperty('type'))

    def setSubWindowLocation(self, title, x, y):
        self.widgetManager.get(WIDGET_NAMES.SubWindow, title).setLocation(x, y)

    def showAllSubWindows(self):
        for sub in self.widgetManager.group(WIDGET_NAMES.SubWindow):
            self.showSubWindow(sub)

    # functions to show/hide/destroy SubWindows
    def showSubWindow(self, title, hide=False, follow=False):
        tl = self.widgetManager.get(WIDGET_NAMES.SubWindow, title)
        if hide:
            self.hideAllSubWindows()
        gui.trace("Showing subWindow %s", title)

        tl.show()
        self._bringToFront(tl)
        tl.block()

        return tl

    def hideAllSubWindows(self, useStopFunction=False):
        for sub in self.widgetManager.group(WIDGET_NAMES.SubWindow):
            self.hideSubWindow(sub, useStopFunction)

    def hideSubWindow(self, title, useStopFunction=False):
        self.widgetManager.get(WIDGET_NAMES.SubWindow, title).hide(useStopFunction)

    def confirmHideSubWindow(self, title):
        self.hideSubWindow(title, True)

    def destroySubWindow(self, title):
        gui.trace("Destroying SubWindow %s", title)
        tl = self.widgetManager.get(WIDGET_NAMES.SubWindow, title)
        tl.prepDestroy()
        # get rid of all the kids!
        self.cleanseWidgets(tl)

    def destroyAllSubWindows(self):
        gui.trace("Destroying all SubWindows")
        keys = list(self.widgetManager.group(WIDGET_NAMES.SubWindow).keys())
        for k in keys:
            gui.trace("Destroying SubWindow: %s", k)
            wi = self.widgetManager.get(WIDGET_NAMES.SubWindow, k)
            self.cleanseWidgets(wi)

        # access has widgets stored in the standard widget store
        self.accessMade = False

#####################################
# END containers
#####################################

    # function to destroy widget & all children
    # will also attempt to remove all trace from config dictionaries
    def cleanseWidgets(self, widget):
        widgType = gui.GET_WIDGET_CLASS(widget)
        gui.trace("Attempting to cleanse: %s", widgType)

        # make sure we've cleansed any children first
        for child in widget.winfo_children():
            self.cleanseWidgets(child)


        if hasattr(widget, 'APPJAR_TYPE'):
            widgType = widget.APPJAR_TYPE
            widgName = WIDGET_NAMES.name(widgType)
            gui.trace("Cleansing: %s", widgName)

            if widgType not in [WIDGET_NAMES.Tab, WIDGET_NAMES.Page]:
                if not self.widgetManager.destroyWidget(widgType, widget):
                    self.warn("Unable to destroy %s, during cleanse - destroy returned False", widgName)

                # must clear the frameLabel's label as well
                if widgType == WIDGET_NAMES.FrameLabel:
                    gui.trace("Also Cleansing: %s", WIDGET_NAMES.name(WIDGET_NAMES.Label))
                    if not self.widgetManager.destroyWidget(WIDGET_NAMES.Label, widget):
                        self.warn("Unable to destroy %s, during cleanse - destroy returned False", WIDGET_NAMES.Label)
            else:
                self.trace("Skipped %s, cleansed by parent", widgType)

            # need to remove if a container
            if widgName in WIDGET_NAMES.containers:
                self.trace("Destroying container: %s", widgName)
                self.widgetManager.destroyContainer(WIDGET_NAMES.ContainerLog, widget)

#        elif widgType in ('CanvasDnd', 'ValidationLabel', 'TabBorder', 'TabContainer', 'TabText', 'BgLabel') or hasattr(widget, 'SKIP_CLEANSE'):
        elif widgType in ('CanvasDnd', 'ValidationLabel', 'Grip',
                            'TabBorder', 'TabContainer', 'TabText', 'BgLabel') \
                            or widget.__dict__.get('SKIP_CLEANSE', False):
            pass # not logged in WidgetManager
        else:
            self.warn("Unable to destroy %s, during cleanse - NO APPJAR TYPE", gui.GET_WIDGET_CLASS(widget))

    # functions to hide & show the main window
    def hide(self, btn=None):
        self._getTopLevel().displayed = False
        self._getTopLevel().withdraw()

    def show(self, btn=None):
        self._getTopLevel().displayed = True
        self._getTopLevel().deiconify()

    def setVisible(self, visible=True):
        if visible: self.show()
        else: self.hide()

    def getVisible(self):
        return self.topLevel.displayed

    visible = property(getVisible, setVisible)


#####################################
# warn when bad functions called...
#####################################
    def __getattr__(self, name):
        def handlerFunction(*args, **kwargs):
            self.warn("Unknown function: <%s> Check your spelling, do you need more camelCase?", name)
        return handlerFunction

    def __setattr__(self, name, value):
        # would this create a new attribute?
        if self.built and not hasattr(self, name):
            raise AttributeError("Creating new attributes is not allowed!")
        super(gui, self).__setattr__(name, value)

#####################################
# LabelBox Functions
#####################################

    # this will build a frame, with a label on the left hand side
    def _getLabelBox(self, title, **kwargs):
        self.widgetManager.verify(WIDGET_NAMES.Label, title)

        label = kwargs.pop('label', title)
        if label is True: label = title
        font = kwargs.pop('font', self._getContainerProperty('labelFont'))

        # first, make a frame
        frame = self._makeLabelBox()(self.getContainer())
        if not self.ttkFlag:
            frame.config(background=self._getContainerBg())
        self.widgetManager.log(WIDGET_NAMES.FrameBox, frame)

        # next make the label
        if self.ttkFlag:
            lab = ttk.Label(frame)
        else:
            lab = Label(frame, background=self._getContainerBg())

        frame.theLabel = lab
        lab.hidden = False
        lab.inContainer = True
        lab.config(
            text=label,
            anchor=W,
            justify=LEFT,
            font=font
        )

        if not self.ttkFlag:
            lab.config(background=self._getContainerBg())
        lab.DEFAULT_TEXT = label

        self.widgetManager.add(WIDGET_NAMES.Label, title, lab)
        self.widgetManager.add(WIDGET_NAMES.FrameLabel, title, lab)

        # now put the label in the frame
        lab.pack(side=LEFT, fill=Y)

        return frame

    # this is where we add the widget to the frame built above
    def _packLabelBox(self, frame, widget):
        widget.pack(side=LEFT, fill=BOTH, expand=True)
        widget.inContainer = True
        frame.theWidget = widget
        #widget.grid( row=0, column=1, sticky=W+E )
        #Grid.columnconfigure(frame, 1, weight=1)
        #Grid.rowconfigure(frame, 0, weight=1)

    # function to resize labels, if they are hidden or shown
    # not using this for two reasons:
    # - doesn't really work when font size changes
    # - breaks when things in containers

# this can be made into a container property

#    def _updateLabelBoxes(self, title, column):
#        if len(title) >= self.labWidth.get(column, -1):
#            self.labWidth[column] = len(title)
#            # loop through other labels and resize
#            for na, wi in self.widgetManager.group(WIDGET_NAMES.FrameLabel).items():
#                col = wi.master.grid_info().get("column", wi.master.master.grid_info().get("column", -1))
#                if int(col) == column:
#                    wi.config(width=self.labWidth[column])

#####################################
# FUNCTION for check boxes
#####################################

    def tick(self, title, value=None, *args, **kwargs):
        """ simpleGUI - shortner for checkBox() """
        return self.checkBox(title, value, *args, **kwargs)

    def check(self, title, value=None, *args, **kwargs):
        """ simpleGUI - shortner for checkBox() """
        return self.checkBox(title, value, *args, **kwargs)

    def checkBox(self, title, value=None, *args, **kwargs):
        """ adds, sets & gets checkBoxes all in one go """
        widgKind = WIDGET_NAMES.CheckBox
        callFunction = kwargs.pop("callFunction", True)
        text = kwargs.pop("text", None)

        try: self.widgetManager.verify(widgKind, title)
        except: #widget exists
            if value is not None: self.setCheckBox(title, ticked=value, callFunction=callFunction)
            check = self.getCheckBox(title)
        else: # new widget
            kwargs = self._parsePos(kwargs.pop("pos", []), kwargs)
            check = self._checkBoxMaker(title, *args, **kwargs)
            if value is not None: self.setCheckBox(title, value)
        if text is not None: self.setCheckBoxText(title, text)

        if len(kwargs) > 0:
            self._configWidget(title, widgKind, **kwargs)
        return check

    def _checkBoxMaker(self, title, value=None, kind="cb", row=None, column=0, colspan=0, rowspan=0, **kwargs):
        """ internal wrapper to hide kwargs from original add functions """
        name = kwargs.pop("name", kwargs.pop('label', None))
        return self.addCheckBox(title, row, column, colspan, rowspan, name)

    def addCheckBox(self, title, row=None, column=0, colspan=0, rowspan=0, name=None):
        ''' adds a new check box, at the specified position '''
        self.widgetManager.verify(WIDGET_NAMES.CheckBox, title)
        var = IntVar(self.topLevel)
        if name is None:
            name = title

        if not self.ttkFlag:
            cb = Checkbutton(self.getContainer(), text=name, variable=var)
            cb.config(
                font=self._getContainerProperty('labelFont'),
                background=self._getContainerBg(),
                activebackground=self._getContainerBg(),
                anchor=W)
        else:
            cb = ttk.Checkbutton(self.getContainer(), text=name, variable=var)

        cb.DEFAULT_TEXT = name
        cb.bind("<Button-1>", self._grabFocus)
        self.widgetManager.add(WIDGET_NAMES.CheckBox, title, cb)
        self.widgetManager.add(WIDGET_NAMES.CheckBox, title, var, group=WidgetManager.VARS)
        self._positionWidget(cb, row, column, colspan, rowspan, EW)
        return cb

    def setCheckBoxText(self, title, text):
        cb = self.widgetManager.get(WIDGET_NAMES.CheckBox, title)
        cb.DEFAULT_TEXT = text
        cb.config(text=text)

    def addNamedCheckBox(self, name, title, row=None, column=0, colspan=0, rowspan=0):
        ''' adds a new check box, at the specified position, with the name as the text '''
        return self.addCheckBox(title, row, column, colspan, rowspan, name)

    def getCheckBox(self, title):
        bVar = self.widgetManager.get(WIDGET_NAMES.CheckBox, title, group=WidgetManager.VARS)
        if bVar.get() == 1:
            return True
        else:
            return False

    def getAllCheckBoxes(self):
        cbs = {}
        for k in self.widgetManager.group(WIDGET_NAMES.CheckBox):
            cbs[k] = self.getCheckBox(k)
        return cbs

    def setCheckBox(self, title, ticked=True, callFunction=True):
        cb = self.widgetManager.get(WIDGET_NAMES.CheckBox, title)
        bVar = self.widgetManager.get(WIDGET_NAMES.CheckBox, title, group=WidgetManager.VARS)
        bVar.set(ticked)
        if ticked:
            if not self.ttkFlag:
                cb.select()
            else:
                cb.state(['selected'])
        else:
            if not self.ttkFlag:
                cb.deselect()
            else:
                cb.state(['!selected'])
        # now call function
        if callFunction:
            if hasattr(cb, 'cmd'):
                cb.cmd()

    def setCheckBoxBoxBg(self, title, newCol):
        self.setCheckBoxSelectColour(title, newCol)

    def setCheckBoxSelectColour(self, title, newCol):
        cb = self.widgetManager.get(WIDGET_NAMES.CheckBox, title)
        cb.config(selectcolor=newCol)

    def clearAllCheckBoxes(self, callFunction=False):
        for cb in self.widgetManager.group(WIDGET_NAMES.CheckBox):
            self.setCheckBox(cb, ticked=False, callFunction=callFunction)

#####################################
# FUNCTION for scales
#####################################

    def slider(self, title, *args, **kwargs):
        """ simpleGUI - alternative for scale() """
        return self.scale(title, *args, **kwargs)

    def scale(self, title, *args, **kwargs):
        """ simpleGUI - adds, sets & gets scales all in one go """
        widgKind = WIDGET_NAMES.Scale

        vert = kwargs.pop("direction", "horizontal").lower() == "vertical"
        increment = kwargs.pop("increment", None)
        value = kwargs.pop("value", None)
        interval = kwargs.pop("interval", None)
        show = kwargs.pop("show", False)
        _range = kwargs.pop("range", None)
        callFunction = kwargs.pop("callFunction", True)
        label = kwargs.pop("label", False)

        try: self.widgetManager.verify(widgKind, title)
        except: # widget exists
            scale = self.getScale(title)
        else: # new widget
            kwargs = self._parsePos(kwargs.pop("pos", []), kwargs)
            scale = self._scaleMaker(title, label, *args, **kwargs)

        if _range is not None: self.setScaleRange(title, _range[0], _range[1])
        if vert: self.setScaleVertical(title)
        if increment is not None: self.setScaleIncrement(title, increment)
        if interval is not None: self.showScaleIntervals(title, interval)
        if show: self.showScaleValue(title)
        if value is not None: self.setScale(title, value, callFunction)

        if len(kwargs) > 0:
            self._configWidget(title, widgKind, **kwargs)

        return scale

    def _buildScale(self, title, frame):
        self.widgetManager.verify(WIDGET_NAMES.Scale, title)
        var = DoubleVar(self.topLevel)
        if not self.ttkFlag:
            scale = self._makeAjScale()(frame, increment=10, variable=var, repeatinterval=10, orient=HORIZONTAL, font=self._getContainerProperty('inputFont'))
            scale.config(digits=1, showvalue=False, highlightthickness=1)
        else:
            scale = self._makeAjScale()(frame, increment=10, variable=var, orient=HORIZONTAL)

        scale.bind("<Button-1>", self._grabFocus, "+")
        scale.var = var
        scale.inContainer = False
        self.widgetManager.add(WIDGET_NAMES.Scale, title, scale)
        return scale

    def _scaleMaker(self, title, label, row=None, column=0, colspan=0, rowspan=0, **kwargs):
        if label: return self.addLabelScale(title, row, column, colspan, rowspan, label)
        else: return self.addScale(title, row, column, colspan, rowspan)

    def addScale(self, title, row=None, column=0, colspan=0, rowspan=0):
        ''' adds a slidable scale at the specified position '''
        scale = self._buildScale(title, self.getContainer())
        self._positionWidget(scale, row, column, colspan, rowspan)
        return scale

    def addLabelScale(self, title, row=None, column=0, colspan=0, rowspan=0, label=True):
        ''' adds a slidable scale, with a label showing the title  at the specified position '''
        frame = self._getLabelBox(title, label=label)
        scale = self._buildScale(title, frame)
        self._packLabelBox(frame, scale)
        self._positionWidget(frame, row, column, colspan, rowspan)
        return scale

    def getScale(self, title):
        sc = self.widgetManager.get(WIDGET_NAMES.Scale, title)
        return sc.get()

    def getAllScales(self):
        scales = {}
        for k in self.widgetManager.group(WIDGET_NAMES.Scale):
            scales[k] = self.getScale(k)
        return scales

    def setScale(self, title, pos, callFunction=True):
        sc = self.widgetManager.get(WIDGET_NAMES.Scale, title)
        with PauseCallFunction(callFunction, sc):
            sc.set(pos)

    def clearAllScales(self, callFunction=False):
        for sc in self.widgetManager.group(WIDGET_NAMES.Scale):
            self.setScale(sc, self.widgetManager.get(WIDGET_NAMES.Scale, sc).cget("from"), callFunction=callFunction)

    def setScaleIncrement(self, title, increment):
        sc = self.widgetManager.get(WIDGET_NAMES.Scale, title)
        sc.increment = increment

    def setScaleLength(self, title, length):
        if not self.ttkFlag:
            sc = self.widgetManager.get(WIDGET_NAMES.Scale, title)
            sc.config(sliderlength=length)
        else:
            self.warn("ttk: setScaleLength() not supported: %s", title)

    # this will make the scale show interval numbers
    # set to 0 to remove
    def showScaleIntervals(self, title, intervals):
        if not self.ttkFlag:
            sc = self.widgetManager.get(WIDGET_NAMES.Scale, title)
            sc.config(tickinterval=intervals)
        else:
            self.warn("ttk: showScaleIntervals() not supported: %s", title)

    # this will make the scale show its value
    def showScaleValue(self, title, show=True):
        if not self.ttkFlag:
            sc = self.widgetManager.get(WIDGET_NAMES.Scale, title)
            sc.config(showvalue=show)
        else:
            self.warn("ttk: showScaleValue() not supported: %s", title)

    # change the orientation (Hor or Vert)
    def setScaleVertical(self, title):
        sc = self.widgetManager.get(WIDGET_NAMES.Scale, title)
        sc.config(orient=VERTICAL)

    def setScaleHorizontal(self, title):
        sc = self.widgetManager.get(WIDGET_NAMES.Scale, title)
        sc.config(orient=HORIZONTAL)

    def setScaleRange(self, title, start, end, curr=None):
        if curr is None:
            curr = start
        sc = self.widgetManager.get(WIDGET_NAMES.Scale, title)
        sc.config(from_=start, to=end)
        self.setScale(title, curr)

        # set the increment as 10%
        try:
            res = sc.cget("resolution")
            diff = int((((end - start)/res)/10)+0.99) # add 0.99 to round up...
            sc.increment = diff
        except:
            pass # resolution not supported in ttk

#####################################
# FUNCTION for optionMenus
#####################################

    def combo(self, title, value=None, *args, **kwargs):
        """ shortner for optionBox() """
        return self.optionBox(title, value, *args, **kwargs)

    def option(self, title, value=None, *args, **kwargs):
        """ simpleGUI - shortner for optionBox() """
        return self.optionBox(title, value, *args, **kwargs)

    def optionbox(self, title, value=None, *args, **kwargs):
        """ simpleGUI - shortner for optionBox() """
        return self.optionBox(title, value, *args, **kwargs)

    def optionBox(self, title, value=None, *args, **kwargs):
        """ simpleGUI - adds, sets & gets optionBoxes all in one go """
        widgKind = WIDGET_NAMES.OptionBox
        kind = kwargs.pop("kind", "standard").lower().strip()
        label = kwargs.pop("label", False)
        callFunction = kwargs.pop("callFunction", True)
        override = kwargs.pop("override", False)
        checked = kwargs.pop("checked", True)
        selected = kwargs.pop("selected", None)
        disabled = kwargs.pop("disabled", "-")

        # select=set, replace=change, rename=rename, clear=clear, delete=delete
        if value is None: mode = 'get'
        else: mode = 'select'
        mode = kwargs.pop("mode", mode)
        index = kwargs.pop("index", None)
        newName = kwargs.pop("newName", None)

        try: self.widgetManager.verify(WIDGET_NAMES.OptionBox, title)
        except: # widget exists
            if mode == "select":
                if value is not None: self.setOptionBox(title, index=value, value=True, callFunction=callFunction, override=override)
                else: gui.error("No item specified to select in optionBox: %s", title)
            elif mode == "deselect":
                if value is not None: self.setOptionBox(title, index=value, value=False, callFunction=callFunction, override=override)
                else:
                    self.clearOptionBox(title, callFunction=callFunction)
                    gui.info("optionBox set back to its original state: %s", title)
            elif mode == "toggle":
                gui.error("Toggling optionboxes not supported: %s", title)
            elif mode == "clear":
                if value is not None: gui.error("No value should be specified wen clearing optionBox: %s", title)
                else: self.clearOptionBox(title, callFunction=callFunction)
            elif mode == "rename":
                if value is not None: self.renameOptionBox(title, item=value, newName=newName, callFunction=callFunction)
                else: gui.error("No item specified to rename in optionBox: %s", title)
            elif mode == "replace":
                if value is not None: self.changeOptionBox(title, options=value, index=index, callFunction=callFunction)
                else: gui.error("No values specified to replace in optionBox: %s", title)
            elif mode == "delete":
                if value is not None: self.deleteOptionBox(title, index=value)
                else: gui.error("No item specified to delete in optionBox: %s", title)
            elif mode == "get":
                pass
            else:
                gui.error("Invalid mode (%s) specified in optionBox: %s", mode, title)

            opt =  self.getOptionBox(title)
        else: # new widget
            kwargs = self._parsePos(kwargs.pop("pos", []), kwargs)
            if kind == "ticks":
                if label: opt = self.addLabelTickOptionBox(title, value, *args, label=label, disabled=disabled, **kwargs)
                else: opt = self.addTickOptionBox(title, value, *args, disabled=disabled, **kwargs)
            else:
                if label: opt = self.addLabelOptionBox(title, value, *args, label=label, disabled=disabled, **kwargs)
                else: opt = self.addOptionBox(title, value, *args, disabled=disabled, **kwargs)
                if selected is not None: self.setOptionBox(title, selected)

        if len(kwargs) > 0:
            self._configWidget(title, widgKind, **kwargs)

        return opt

    def addDbOptionBox(self, title, db, row=None, column=0, colspan=0, rowspan=0, **kwargs):
        ''' adds an option box, with a list of tables form the specified database '''
        data = self._getDbTables(db)
        opt = self.option(title, data, row, column, colspan, rowspan, **kwargs)
        opt.db = db
        return opt

    def _buildOptionBox(self, frame, title, options, kind="normal", disabled='-'):
        """ Internal wrapper, used for building OptionBoxes.
        It will use the kind to choose either a standard OptionBox or a TickOptionBox.
        ref: http://stackoverflow.com/questions/29019760/how-to-create-a-combobox-that-includes-checkbox-for-each-item

        :param frame: this should be a container, used as the parent for the OptionBox
        :param title: the key used to reference this OptionBox
        :param options: a list of values to put in the OptionBox, can be len 0
        :param kind: the style of OptionBox: notmal or ticks
        :returns: the created OptionBox
        :raises ItemLookupError: if the title is already in use
        """
        self.widgetManager.verify(WIDGET_NAMES.OptionBox, title)

        # create a string var to hold selected item
        var = StringVar(self.topLevel)
        self.widgetManager.add(WIDGET_NAMES.OptionBox, title, var, group=WidgetManager.VARS)

        maxSize, options = self._configOptionBoxList(title, options, kind)

        if len(options) > 0 and kind == "normal":
            option = ajOption(frame, var, *options)
            var.set(options[0])
            option.kind = "normal"

        elif kind == "ticks":
            option = ajOption(frame, variable=var, value="")
            self._buildTickOptionBox(title, option, options)
        else:
            option = ajOption(frame, var, [])
            option.kind = "normal"

        option.config(
            justify=LEFT,
            font=self._getContainerProperty('inputFont'),
#            background=self._getContainerBg(),
            highlightthickness=0,
            width=maxSize,
            takefocus=1)
        option.bind("<Button-1>", self._grabFocus)

        # compare on windows & mac
        #option.config(highlightthickness=12, bd=0, highlightbackground=self._getContainerBg())
        option.var = var
        option.maxSize = maxSize
        option.inContainer = False
        option.options = options
        option.disabled = disabled

        option.DEFAULT_TEXT=""
        if options is not None:
            option.DEFAULT_TEXT='\n'.join(str(x) for x in options)

#        if self.platform == self.MAC:
#            option.config(highlightbackground=self._getContainerBg())

        option.bind("<Tab>", self._focusNextWindow)
        option.bind("<Shift-Tab>", self._focusLastWindow)

        # add a right click menu
        self._addRightClickMenu(option)

        # disable any separators
        self._disableOptionBoxSeparators(option)

        # add to array list
        self.widgetManager.add(WIDGET_NAMES.OptionBox, title, option)
        return option

    def _buildTickOptionBox(self, title, option, options):
        """ Internal wrapper, used for building TickOptionBoxes.
        Called by _buildOptionBox & changeOptionBox.
        Will add each of the options as a tick box, and use the title as a disabled header.

        :param title: the key used to reference this OptionBox
        :param option: an existing OptionBox that will be emptied & repopulated
        :param options: a list of values to put in the OptionBox, can be len 0
        :returns: None - the option param is modified
        :raises ItemLookupError: if the title can't be found
        """
        # delete any items - either the initial one when created, or any existing ones if changing
        option['menu'].delete(0, 'end')
        var = self.widgetManager.get(WIDGET_NAMES.OptionBox, title, group=WidgetManager.VARS)
        var.set(title)
        vals = {}
        for o in options:
            vals[o] = BooleanVar()
            option['menu'].add_checkbutton( label=o, onvalue=True, offvalue=False, variable=vals[o])
        self.widgetManager.update(WIDGET_NAMES.TickOptionBox, title, vals, group=WidgetManager.VARS)
        option.kind = "ticks"

    def addOptionBox(self, title, options, row=None, column=0, colspan=0, rowspan=0, disabled='-', **kwargs):
        """ Adds a new standard OptionBox.
        Simply calls internal function _buildOptionBox.

        :param title: the key used to reference this OptionBox
        :param options: a list of values to put in the OptionBox, can be len 0
        :returns: the created OptionBox
        :raises ItemLookupError: if the title is already in use
        """
        option = self._buildOptionBox(self.getContainer(), title, options, disabled=disabled)
        self._positionWidget(option, row, column, colspan, rowspan)
        return option

    def addLabelOptionBox(self, title, options, row=None, column=0, colspan=0, rowspan=0, disabled="-", **kwargs):
        """ Adds a new standard OptionBox, with a Label before it.
        Simply calls internal function _buildOptionBox, placing it in a LabelBox.

        :param title: the key used to reference this OptionBox and text for the Label
        :param options: a list of values to put in the OptionBox, can be len 0
        :returns: the created OptionBox (not the LabelBox)
        :raises ItemLookupError: if the title is already in use
        """
        frame = self._getLabelBox(title, **kwargs)
        option = self._buildOptionBox(frame, title, options, disabled=disabled)
        self._packLabelBox(frame, option)
        self._positionWidget(frame, row, column, colspan, rowspan)
        return option

    def addTickOptionBox(self, title, options, row=None, column=0, colspan=0, rowspan=0, disabled="-", **kwargs):
        """ Adds a new TickOptionBox.
        Simply calls internal function _buildOptionBox.

        :param title: the key used to reference this TickOptionBox
        :param options: a list of values to put in the TickOptionBox, can be len 0
        :returns: the created TickOptionBox
        :raises ItemLookupError: if the title is already in use
        """
        tick = self._buildOptionBox(self.getContainer(), title, options, kind="ticks", disabled=disabled)
        self._positionWidget(tick, row, column, colspan, rowspan)
        return tick

    def addLabelTickOptionBox(self, title, options, row=None, column=0, colspan=0, rowspan=0, disabled="-", **kwargs):
        """ Adds a new TickOptionBox, with a Label before it
        Simply calls internal function _buildOptionBox, placing it in a LabelBox

        :param title: the key used to reference this TickOptionBox, and text for the Label
        :param options: a list of values to put in the TickOptionBox, can be len 0
        :returns: the created TickOptionBox (not the LabelBox)
        :raises ItemLookupError: if the title is already in use
        """
        frame = self._getLabelBox(title, **kwargs)
        tick = self._buildOptionBox(frame, title, options, kind="ticks", disabled=disabled)
        self._packLabelBox(frame, tick)
        self._positionWidget(frame, row, column, colspan, rowspan)
        return tick

    def getOptionBox(self, title):
        """ Gets the selected item from the named OptionBox

        :param title: the OptionBox to check
        :returns: the selected item in an OptionBox or a dictionary of all items and their status for a TickOptionBox
        :raises ItemLookupError: if the title can't be found
        """
        box = self.widgetManager.get(WIDGET_NAMES.OptionBox, title)

        if box.kind == "ticks":
            val = self.widgetManager.get(WIDGET_NAMES.TickOptionBox, title, group=WidgetManager.VARS)
            retVal = {}
            for k, v in val.items():
                retVal[k] = bool(v.get())
            return retVal
        else:
            val = self.widgetManager.get(WIDGET_NAMES.OptionBox, title, group=WidgetManager.VARS)
            val = val.get().strip()
            # set to None if it's a divider
            if val.startswith("-") or len(val) == 0:
                val = None
            return val

    def getAllOptionBoxes(self):
        """ Convenience function to get the selected items for all OptionBoxes in the GUI.

        :returns: a dictionary containing the result of calling getOptionBox for every OptionBox/TickOptionBox in the GUI
        """
        boxes = {}
        for k in self.widgetManager.group(WIDGET_NAMES.OptionBox):
            boxes[k] = self.getOptionBox(k)
        return boxes

    def _disableOptionBoxSeparators(self, box):
        """ Loops through all items in box and if they start with a dash, disables them

        :param box: the OptionBox to process
        :returns: None
        """
        for pos, item in enumerate(box.options):
            if item.startswith(box.disabled):
                box["menu"].entryconfigure(pos, state="disabled")
            else:
                box["menu"].entryconfigure(pos, state="normal")

    def _configOptionBoxList(self, title, options, kind):
        """ Tidies up the list provided when an OptionBox is created/changed

        :param title: the title for the OptionBox - only used by TickOptionBox to calculate max size
        :param options: the list to tidy
        :param kind: The kind of option box (normal or ticks)
        :returns: a tuple containing the maxSize (width) and tidied list of items
        """

        # deal with a dict_keys object - messy!!!!
        if not isinstance(options, list):
            options = list(options)

        # make sure all options are strings
        options = [str(i) for i in options]

        # check for empty strings, replace first with message, remove rest
        found = False
        newOptions = []
        for pos, item in enumerate(options):
            if str(item).strip() == "":
                if not found:
                    newOptions.append("- options -")
                    found = True
            else:
                newOptions.append(item)

        options = newOptions

        # get the longest string length
        try:
            maxSize = len(str(max(options, key=len)))
        except:
            try:
                maxSize = len(str(max(options)))
            except:
                maxSize = 0

        # increase if ticks
        if kind == "ticks":
            if len(title) > maxSize:
                maxSize = len(title)

        # new bug?!? - doesn't fit anymore!
        if self.platform == self.MAC:
            maxSize += 3
        return maxSize, options

    def changeOptionBox(self, title, options, index=None, callFunction=False):
        """ Changes the entire contents of the named OptionBox
        ref: http://www.prasannatech.net/2009/06/tkinter-optionmenu-changing-choices.html

        :param title: the OptionBox to change
        :param options: the new values to put in the OptionBox
        :param index: an optional initial value to select
        :param callFunction: whether to generate an event to notify that the widget has changed
        :returns: None
        :raises ItemLookupError: if the title can't be found
        """
        # get the optionBox & associated var
        box = self.widgetManager.get(WIDGET_NAMES.OptionBox, title)

        # tidy up list and get max size
        maxSize, options = self._configOptionBoxList(title, options, "normal")

        # warn if new options bigger
        if maxSize > box.maxSize:
            self.warn("The new options are wider then the old ones: %s > %s", maxSize, box.maxSize)

        if box.kind == "ticks":
            self._buildTickOptionBox(title, box, options)
        else:
            # delete the current options
            box['menu'].delete(0, 'end')

            # add the new items
            for option in options:
                box["menu"].add_command(
                    label=option, command=lambda temp=option: box.setvar(
                        box.cget("textvariable"), value=temp))

            with PauseCallFunction(callFunction, box):
                box.var.set(options[0])

        box.options = options

        # disable any separators
        self._disableOptionBoxSeparators(box)
        # select the specified option
        self.setOptionBox(title, index, callFunction=False, override=True)

    def deleteOptionBox(self, title, index):
        """ Deleted the specified item from the named OptionBox

        :param title: the OptionBox to change
        :param inde: the value to delete - either a numeric index, or the text of an item
        :returns: None
        :raises ItemLookupError: if the title can't be found
        """
        self.widgetManager.check(WIDGET_NAMES.OptionBox, title, group=WidgetManager.VARS)
        self.setOptionBox(title, index, value=None, override=True)

    def renameOptionBoxItem(self, title, item, newName=None, callFunction=False):
        """ Changes the text of the specified item in the named OptionBox
        :param title: the OptionBox to change
        :param item: the item to rename
        :param newName: the value to rename it with
        :param callFunction: whether to generate an event to notify that the widget has changed
        :returns: None
        :raises ItemLookupError: if the title can't be found
        """
        self.widgetManager.check(WIDGET_NAMES.OptionBox, title, group=WidgetManager.VARS)
        self.setOptionBox(title, item, value=newName, callFunction=callFunction)

    def clearOptionBox(self, title, callFunction=True):
        """ Deselects any items selected in the named OptionBox
        If a TickOptionBox, all items will be set to False (unticked)

        :param title: the OptionBox to change
        :param callFunction: whether to generate an event to notify that the widget has changed
        :returns: None
        :raises ItemLookupError: if the title can't be found
        """
        box = self.widgetManager.get(WIDGET_NAMES.OptionBox, title)
        if box.kind == "ticks":
            # loop through each tick, set it to False
            ticks = self.widgetManager.get(WIDGET_NAMES.TickOptionBox, title, group=WidgetManager.VARS)
            for k in ticks:
                self.setOptionBox(title, k, False, callFunction=callFunction)
        else:
            self.setOptionBox(title, 0, callFunction=callFunction, override=True)

    def clearAllOptionBoxes(self, callFunction=False):
        """ Convenience function to clear all OptionBoxes in the GUI
        Will simply call clearOptionBox on each OptionBox/TickOptionBox

        :param callFunction: whether to generate an event to notify that the widget has changed
        :returns: None
        """
        for k in self.widgetManager.group(WIDGET_NAMES.OptionBox):
            self.clearOptionBox(k, callFunction)

    def setOptionBoxDisabledChar(self, title, disabled="-"):
        box = self.widgetManager.get(WIDGET_NAMES.OptionBox, title)
        box.disabled = disabled
        self._disableOptionBoxSeparators(box)

    def setOptionBox(self, title, index, value=True, callFunction=True, override=False):
        """ Main purpose is to select/deselect the item at the specified position
        But will also: delete an item if value is set to None or rename an item if value is set to a String

        :param title: the OptionBox to change
        :param index: the position or value of the item to select/delete
        :param value: determines what to do to the item: if set to None, will delete the item, else it sets the items state
        :param callFunction: whether to generate an event to notify that the widget has changed
        :param override: if set to True, allows a disabled item to be selected
        :returns: None
        :raises ItemLookupError: if the title can't be found
        """
        box = self.widgetManager.get(WIDGET_NAMES.OptionBox, title)

        if box.kind == "ticks":
            gui.trace("Updating tickOptionBox")
            ticks = self.widgetManager.get(WIDGET_NAMES.TickOptionBox, title, group=WidgetManager.VARS)
            if index is None:
                gui.trace("Index empty - nothing to update")
                return
            elif index in ticks:
                gui.trace("Updating: %s", index)

                tick = ticks[index]
                try:
                    index_num = box.options.index(index)
                except:
                    self.warn("Unknown tick: %s in OptionBox: %s", index, title)
                    return

                with PauseCallFunction(callFunction, tick, useVar=False):
                    if value is None: # then we need to delete it
                        gui.trace("Deleting tick: %s from OptionBox %s", index, title)
                        box['menu'].delete(index_num)
                        del(box.options[index_num])
                        self.widgetManager.remove(WIDGET_NAMES.TickOptionBox, title, index, group=WidgetManager.VARS)
                    elif isinstance(value, bool):
                        gui.trace("Updating tick: %s from OptionBox: %s to: %s", index, title, value)
                        tick.set(value)
                    else:
                        gui.trace("Renaming tick: %s from OptionBox: %s to: %s", index, title, value)
                        ticks = self.widgetManager.get(WIDGET_NAMES.TickOptionBox, title, group=WidgetManager.VARS)
                        ticks[value] = ticks.pop(index)
                        box.options[index_num] = value
                        self.changeOptionBox(title, box.options)
                        for tick in ticks:
                            self.widgetManager.get(WIDGET_NAMES.TickOptionBox, title, group=WidgetManager.VARS)[tick].set(ticks[tick].get())
            else:
                if value is None:
                    self.warn("Unknown tick in deleteOptionBox: %s in OptionBox: %s" , index, title)
                else:
                    self.warn("Unknown tick in setOptionBox: %s in OptionBox: %s", index, title)
        else:
            gui.trace("Updating regular optionBox: %s at: %s to: %s", title, index, value)
            count = len(box.options)
            if count > 0:
                if index is None:
                    index = 0
                if not isinstance(index, int):
                    try:
                        index = box.options.index(index)
                    except:
                        if value is None:
                            self.warn("Unknown option in deleteOptionBox: %s in OptionBox: %s", index, title)
                        else:
                            self.warn("Unknown option in setOptionBox: %s in OptionBox: %s", index, title)
                        return

                gui.trace("--> index now: %s", index)

                if index < 0 or index > count - 1:
                    self.warn("Invalid option: %s. Should be between 0 and %s." , count-1, index)
                else:
                    if value is None: # then we can delete it...
                        gui.trace("Deleting option: %s from OptionBox: %s", index, title)
                        box['menu'].delete(index)
                        del(box.options[index])
                        self.setOptionBox(title, 0, callFunction=False, override=override)
                    elif isinstance(value, bool):
                        gui.trace("Updating: OptionBox: %s to: %s", title, index)
                        with PauseCallFunction(callFunction, box):
                            if not box['menu'].invoke(index):
                                if override:
                                    gui.trace("Setting OptionBox: %s to disabled option: %s", title, index)
                                    box["menu"].entryconfigure(index, state="normal")
                                    box['menu'].invoke(index)
                                    box["menu"].entryconfigure(index, state="disabled")
                                else:
                                    self.warn("Unable to set disabled option: %s in OptionBox %s. Try setting 'override=True'", index, title)
                            else:
                                gui.trace("Invoked item: %s", index)
                    else:
                        gui.trace("Renaming: %s from OptionBox: %s to: %s", index, title, value)
                        pos = box.options.index(self.widgetManager.get(WIDGET_NAMES.OptionBox, title, group=WidgetManager.VARS).get())
                        box.options[index] = value
                        self.changeOptionBox(title, box.options, pos)

            else:
                self.widgetManager.get(WIDGET_NAMES.OptionBox, title, group=WidgetManager.VARS).set("")
                self.warn("No items to select from: %s", title)

#####################################
# FUNCTION for GoogleMaps
#####################################

    def map(self, title, value=None, *args, **kwargs):
        """ simpleGUI - adds, sets & gets maps all in one go """
        widgKind = WIDGET_NAMES.Map
        zoom = kwargs.pop("zoom", None)
        size = kwargs.pop("size", None)
        terrain = kwargs.pop("terrain", None)
        proxy = kwargs.pop("proxy", None)

        try: self.widgetManager.verify(widgKind, title)
        except: # widget exists
            gMap = self.getLabel(title)
        else: # new widget
            kwargs = self._parsePos(kwargs.pop("pos", []), kwargs)
            gMap = self.addGoogleMap(title, *args, **kwargs)

        if value is not None: self.setGoogleMapLocation(title, value)
        if zoom is not None: self.setGoogleMapZoom(title, zoom)
        if size is not None: self.setGoogleMapSize(title, size)
        if terrain is not None: self.setGoogleMapTerrain(title, terrain)
        if proxy is not None: self.setGoogleMapProxy(title, proxy)

        if len(kwargs) > 0:
            self._configWidget(title, widgKind, **kwargs)
        return gMap

    def addGoogleMap(self, title, row=None, column=0, colspan=0, rowspan=0):
        ''' adds a GoogleMap widget at the specified position '''
        self._loadURL()
        self._loadTooltip()
        if urlencode is False:
            raise Exception("Unable to load GoogleMaps - urlencode library not available")
        self.widgetManager.verify(WIDGET_NAMES.Map, title)
        gMap = GoogleMap(self.getContainer(), self, useTtk = self.ttkFlag, font=self._getContainerProperty('labelFont'))
        self._positionWidget(gMap, row, column, colspan, rowspan)
        self.widgetManager.add(WIDGET_NAMES.Map, title, gMap)
        return gMap

    def setGoogleMapProxy(self, title, proxyString):
        gMap = self.widgetManager.get(WIDGET_NAMES.Map, title)
        gMap.setProxyString(proxyString)

    def setGoogleMapLocation(self, title, location):
        self.searchGoogleMap(title, location)

    def searchGoogleMap(self, title, location):
        gMap = self.widgetManager.get(WIDGET_NAMES.Map, title)
        gMap.changeLocation(location)

    def setGoogleMapTerrain(self, title, terrain):
        gMap = self.widgetManager.get(WIDGET_NAMES.Map, title)
        if terrain not in gMap.TERRAINS:
            raise Exception("Invalid terrain. Must be one of " + str(gMap.TERRAINS))
        gMap.changeTerrain(terrain)

    def setGoogleMapZoom(self, title, mod):
        self. zoomGoogleMap(title, mod)

    def zoomGoogleMap(self, title, mod):
        gMap = self.widgetManager.get(WIDGET_NAMES.Map, title)
        if mod in ["+", "-"]:
            gMap.zoom(mod)
        elif isinstance(mod, int) and 0 <= mod <= 22:
            gMap.setZoom(mod)

    def setGoogleMapSize(self, title, size):
        gMap = self.widgetManager.get(WIDGET_NAMES.Map, title)
        gMap.setSize(size)

    def setGoogleMapMarker(self, title, location, size=None, colour=None, label=None, replace=False):
        gMap = self.widgetManager.get(WIDGET_NAMES.Map, title)
        if len(location) == 0:
            gMap.removeMarkers()
        else:
            gMap.addMarker(location, size, colour, label, replace)

    def removeGoogleMapMarker(self, title, label):
        gMap = self.widgetManager.get(WIDGET_NAMES.Map, title)
        if len(label) == 0:
            gMap.removeMarkers()
        else:
            gMap.removeMarker(label)

    def getGoogleMapZoom(self, title):
        return self.widgetManager.get(WIDGET_NAMES.Map, title).params["zoom"]

    def getGoogleMapTerrain(self, title):
        return self.widgetManager.get(WIDGET_NAMES.Map, title).params["maptype"].title()

    def getGoogleMapLocation(self, title):
        return self.widgetManager.get(WIDGET_NAMES.Map, title).params["center"]

    def getGoogleMapSize(self, title):
        return self.widgetManager.get(WIDGET_NAMES.Map, title).params["size"]

    def saveGoogleMap(self, title, fileLocation):
        gMap = self.widgetManager.get(WIDGET_NAMES.Map, title)
        return gMap.saveTile(fileLocation)

#####################################
# FUNCTION for matplotlib
#####################################

    def plot(self, title, t=None, s=None, *args, **kwargs):
        """ simpleGUI - adds, sets & gets plots all in one go """
        widgKind = WIDGET_NAMES.Plot
        nav = kwargs.pop("nav", kwargs.pop("showNav", False))

        try: self.widgetManager.verify(widgKind, title)
        except: # widget exists
            keepLabels = kwargs.pop("keepLabels", False)
            self.updatePlot(title, t, s, keepLabels=keepLabels)
            plot = self.widgetManager.get(WIDGET_NAMES.Plot, title).axes
        else: # new widget
            kwargs = self._parsePos(kwargs.pop("pos", []), kwargs)
            if t is not None:
                if s is not None:
                    plot = self.addPlot(title, t, s, *args, showNav=nav, **kwargs)
                else:
                    gui.warn("Invalid parameters for plot: must provide t & s")
                    return None
            else:
                plot = self.addPlotFig(title, *args, showNav=nav, **kwargs)

        return plot

    def addPlot(self, title, t, s, row=None, column=0, colspan=0, rowspan=0, width=None, height=None, showNav=False):
        ''' adds a MatPlotLib, with t/s plotted '''
        canvas, fig = self._addPlotFig(title, row, column, colspan, rowspan, width, height, showNav)
        axes = fig.add_subplot(111)
        axes.plot(t,s)
        canvas.axes = axes
        return axes

    def addPlotFig(self, title, row=None, column=0, colspan=0, rowspan=0, width=None, height=None, showNav=False):
        canvas, fig = self._addPlotFig(title, row, column, colspan, rowspan, width, height, showNav)
        return fig

    def _addPlotFig(self, title, row=None, column=0, colspan=0, rowspan=0, width=None, height=None, showNav=False):
        self.widgetManager.verify(WIDGET_NAMES.Plot, title)
        self._loadMatplotlib()
        if PlotCanvas is False:
            raise Exception("Unable to load MatPlotLib - plots not available")
        else:
            fig = PlotFig(tight_layout=True)

            if width is not None and height is not None:
                fig.set_size_inches(width,height,forward=True)

            frame = frameBase(self.getContainer())

            canvas = PlotCanvas(fig, frame)
            canvas._tkcanvas.config(background="#c0c0c0", borderwidth=0, highlightthickness=0)
            canvas.fig = fig
            canvas.draw()
            if showNav:
                navBar = PlotNav(canvas, frame)
                navBar.pack(side=TOP, fill=X, expand=0)
            canvas._tkcanvas.pack(side=TOP, fill=BOTH, expand=1)

#            self._positionWidget(canvas.get_tk_widget(), row, column, colspan, rowspan)
            self._positionWidget(frame, row, column, colspan, rowspan, sticky='news')
            self.widgetManager.add(WIDGET_NAMES.Plot, title, canvas)
            return canvas, fig

    def refreshPlot(self, title):
        canvas = self.widgetManager.get(WIDGET_NAMES.Plot, title)
        canvas.draw()

    def updatePlot(self, title, t, s, keepLabels=False):
        axes = self.widgetManager.get(WIDGET_NAMES.Plot, title).axes

        if keepLabels:
            xLab = axes.get_xlabel()
            yLab = axes.get_ylabel()
            pTitle = axes.get_title()
            handles, legends = axes.get_legend_handles_labels()

        axes.clear()
        axes.plot(t, s)

        if keepLabels:
            axes.set_xlabel(xLab)
            axes.set_ylabel(yLab)
            axes.set_title(pTitle)
            axes.legend(handles, legends)

        self.refreshPlot(title)
        return axes


#####################################
# FUNCTION to manage Properties Widgets
#####################################

    def properties(self, title, value=None, *args, **kwargs):
        """ simpleGUI - adds, sets & gets properties all in one go """
        widgKind = WIDGET_NAMES.Properties
        try: self.widgetManager.verify(widgKind, title)
        except: # widget exists
#            if value is not None:
# need to work out args...
#                self.setProperty(title, prop=value)
            props = self.getProperties(title)
        else: # new widget
            kwargs = self._parsePos(kwargs.pop("pos", []), kwargs)
            props = self.addProperties(title, value, *args, **kwargs)

        if len(kwargs) > 0:
            self._configWidget(title, widgKind, **kwargs)
        return props

    def addProperties(self, title, values=None, row=None, column=0, colspan=0, rowspan=0, **kwargs):
        ''' adds a new properties widget, displaying the dictionary of booleans as tick boxes '''
        self.widgetManager.verify(WIDGET_NAMES.Properties, title)
        haveTitle = True
        if self._getContainerProperty('type') == WIDGET_NAMES.ToggleFrame:
            self.containerStack[-1]['sticky'] = "ew"
            haveTitle = False

        props = Properties(self.getContainer(), title, values, haveTitle,
                            font=self._getContainerProperty('labelFont'), background=self._getContainerBg())
        self._positionWidget(props, row, column, colspan, rowspan)
        self.widgetManager.add(WIDGET_NAMES.Properties, title, props)
        return props

    def getProperties(self, title):
        props = self.widgetManager.get(WIDGET_NAMES.Properties, title)
        return props.getProperties()

    def getAllProperties(self):
        props = {}
        for k in self.widgetManager.group(WIDGET_NAMES.Properties):
            props[k] = self.getProperties(k)
        return props

    def getProperty(self, title, prop):
        props = self.widgetManager.get(WIDGET_NAMES.Properties, title)
        return props.getProperty(prop)

    def setProperty(self, title, prop, value=False, callFunction=True):
        props = self.widgetManager.get(WIDGET_NAMES.Properties, title)
        props.addProperty(prop, value, callFunction=callFunction)

    def setProperties(self, title, props, callFunction=True):
        p = self.widgetManager.get(WIDGET_NAMES.Properties, title)
        p.addProperties(props, callFunction=callFunction)

    def deleteProperty(self, title, prop):
        props = self.widgetManager.get(WIDGET_NAMES.Properties, title)
        props.addProperty(prop, None, callFunction=False)

    def setPropertyText(self, title, prop, newText=None):
        props = self.widgetManager.get(WIDGET_NAMES.Properties, title)
        props.renameProperty(prop, newText)

    def setPropertiesBoxBg(self, title, newCol):
        self.setPropertiesSelectColour(title, newCol)

    def setPropertiesSelectColour(self, title, newCol):
        props = self.widgetManager.get(WIDGET_NAMES.Properties, title)
        props.config(selectcolor=newCol)

    def clearProperties(self, title, callFunction=True):
        props = self.widgetManager.get(WIDGET_NAMES.Properties, title)
        props.clearProperties(callFunction)

    def clearAllProperties(self, callFunction=False):
        props = {}
        for k in self.widgetManager.group(WIDGET_NAMES.Properties):
            self.clearProperties(k, callFunction)

    def resetProperties(self, title, callFunction=True):
        props = self.widgetManager.get(WIDGET_NAMES.Properties, title)
        props.resetProperties(callFunction)

    def resetAllProperties(self, callFunction=False):
        props = {}
        for k in self.widgetManager.group(WIDGET_NAMES.Properties):
            self.resetProperties(k, callFunction)

#####################################
# FUNCTION to add spin boxes
#####################################

    def spin(self, title, value=None, *args, **kwargs):
        """ simpleGUI - shortner for spinBox() """
        return self.spinBox(title, value, *args, **kwargs)

    def spinbox(self, title, value=None, *args, **kwargs):
        """ simpleGUI - shortner for spinBox() """
        return self.spinBox(title, value, *args, **kwargs)

    def spinBox(self, title, value=None, *args, **kwargs):
        """ simpleGUI - adds, sets & gets spinBoxes all in one go """
        widgKind = WIDGET_NAMES.SpinBox

        endValue = kwargs.pop("endValue", None)
        selected = kwargs.pop("selected", None)
        item = kwargs.pop("item", None)
        label = kwargs.pop("label", False)

        # select=select, deselect=<RESET>, toggle=<NONE>, clear=??, rename=set, replace=update, delete=remov
        if value is None: mode = 'get'
        else: mode = 'select'
        mode = kwargs.pop("mode", mode)
        callFunction = kwargs.pop("callFunction", True)

        try: self.widgetManager.verify(widgKind, title)
        except: # widget exists
            if mode == "select":
                if value is not None: self.setSpinBoxPos(title, value, *args, **kwargs)
                else: gui.error("No item specified to select in spinbox: %s", title)
            elif mode == "toggle":
                gui.error("%s not available on spinbox: %s", mode, title)
            elif mode in["clear", "deselect"]:
                self.clearSpinBox(title)
            elif mode == "rename":
                gui.error("%s not implemented yet in spinbox: %s", mode, title)
            elif mode == "replace":
                if value is not None: self.changeSpinBox(title, vals=value)
                else: gui.error("No values specified to replace in spinbox: %s", title)
            elif mode == "delete":
                gui.error("%s not implemented yet in spinbox: %s", mode, title)
            elif mode == "get":
                pass
            else:
                gui.error("Invalid mode (%s) specified in spinbox: %s", mode, title)

            spinBox =  self.getSpinBox(title)
        else: # new widget
            kwargs = self._parsePos(kwargs.pop("pos", []), kwargs)
            if endValue is not None:
                if label: spinBox = self.addLabelSpinBoxRange(title, value, endValue, *args, label=label, **kwargs)
                else: spinBox = self.addSpinBoxRange(title, value, endValue, *args, **kwargs)
            else:
                if label: spinBox = self.addLabelSpinBox(title, value, *args, label=label, **kwargs)
                else: spinBox = self.addSpinBox(title, value, *args, **kwargs)

        if selected is not None: self.setSpinBoxPos(title, selected)
        if item is not None: self.setSpinBox(title, item)

        if len(kwargs) > 0:
            self._configWidget(title, widgKind, **kwargs)

        return spinBox

    def _buildSpinBox(self, frame, title, vals):
        self.widgetManager.verify(WIDGET_NAMES.SpinBox, title)
        if type(vals) not in [list, tuple]:
            raise Exception("Can't create SpinBox " + title + ". Invalid values: " + str(vals))

        spin = Spinbox(frame)
        spin.var = StringVar(self.topLevel)
        spin.config(textvariable=spin.var)
        spin.inContainer = False
        spin.isRange = False
        spin.config(font=self._getContainerProperty('inputFont'), highlightthickness=0)

# adds bg colour under spinners
#        if self.platform == self.MAC:
#              spin.config(highlightbackground=self._getContainerBg())

        spin.bind("<Tab>", self._focusNextWindow)
        spin.bind("<Shift-Tab>", self._focusLastWindow)

        # store the vals in DEFAULT_TEXT
        spin.DEFAULT_TEXT=""
        if vals is not None:
            spin.DEFAULT_TEXT='\n'.join(str(x) for x in vals)

        self._populateSpinBox(spin, vals)

        # prevent invalid entries
        if self.validateSpinBox is None:
            self.validateSpinBox = (
                self.containerStack[0]['container'].register(
                    self._validateSpinBox), '%P', '%W')

        spin.config(validate='all', validatecommand=self.validateSpinBox)

        self.widgetManager.add(WIDGET_NAMES.SpinBox, title, spin)
        return spin

    def _populateSpinBox(self, spin, vals, reverse=True):
        # make sure it's a list
        # reverse it, so the spin box functions properly
        if reverse:
            vals = list(vals)
            vals.reverse()
        vals = tuple(vals)
        spin.config(values=vals)

    def _addSpinBox(self, title, values, row=None, column=0, colspan=0, rowspan=0):
        spin = self._buildSpinBox(self.getContainer(), title, values)
        self._positionWidget(spin, row, column, colspan, rowspan)
        self.setSpinBoxPos(title, 0)
        return spin

    def addSpinBox(self, title, values, row=None, column=0, colspan=0, rowspan=0, **kwargs):
        ''' adds a spinbox, with the specified values '''
        return self._addSpinBox(title, values, row, column, colspan, rowspan)

    def addLabelSpinBox(self, title, values, row=None, column=0, colspan=0, rowspan=0, **kwargs):
        ''' adds a spinbox, with the specified values, and a label displaying the title '''
        frame = self._getLabelBox(title, **kwargs)
        spin = self._buildSpinBox(frame, title, values)
        self._packLabelBox(frame, spin)
        self._positionWidget(frame, row, column, colspan, rowspan)
        self.setSpinBoxPos(title, 0)
        return spin

    def addSpinBoxRange(self, title, fromVal, toVal, row=None, column=0, colspan=0, rowspan=0, **kwargs):
        ''' adds a spinbox, with a range of whole numbers '''
        vals = list(range(fromVal, toVal + 1))
        spin = self._addSpinBox(title, vals, row, column, colspan, rowspan)
        spin.isRange = True
        return spin

    def addLabelSpinBoxRange(self, title, fromVal, toVal, row=None, column=0, colspan=0, rowspan=0, label=True, **kwargs):
        ''' adds a spinbox, with a range of whole numbers, and a label displaying the title '''
        vals = list(range(fromVal, toVal + 1))
        spin = self.addLabelSpinBox(title, vals, row, column, colspan, rowspan, label=label)
        spin.isRange = True
        return spin

    def getSpinBox(self, title):
        spin = self.widgetManager.get(WIDGET_NAMES.SpinBox, title)
        return spin.get()

    def getAllSpinBoxes(self):
        boxes = {}
        for k in self.widgetManager.group(WIDGET_NAMES.SpinBox):
            boxes[k] = self.getSpinBox(k)
        return boxes

    # validates that an item in the named spinbox starts with the user_input
    def _validateSpinBox(self, user_input, widget_name):
        spin = self.containerStack[0]['container'].nametowidget(widget_name)

        vals = spin.cget("values")  # .split()
        vals = self._getSpinBoxValsAsList(vals)
        for i in vals:
            if i.startswith(user_input):
                return True

        self.containerStack[0]['container'].bell()
        return False

    # expects a valid spin box widget, and a valid value
    def _setSpinBoxVal(self, spin, val, callFunction=True):
        # now call function
        with PauseCallFunction(callFunction, spin):
            spin.var.set(val)

    # is it going to be a hash or list??
    def _getSpinBoxValsAsList(self, vals):
        vals.replace("{", "")
        vals.replace("}", "")
#        if "{" in vals:
#            vals = vals[1:-1]
#            vals = vals.split("} {")
#        else:
        vals = vals.split()
        return vals

    def setSpinBox(self, title, value, callFunction=True):
        spin = self.widgetManager.get(WIDGET_NAMES.SpinBox, title)
        vals = spin.cget("values")  # .split()
        vals = self._getSpinBoxValsAsList(vals)
        val = str(value)
        if val not in vals:
            raise Exception( "Invalid value: " + val + ". Not in SpinBox: " +
                        title + "=" + str(vals))
        self._setSpinBoxVal(spin, val, callFunction)

    def clearSpinBox(self, title, callFunction=False):
        self.setSpinBoxPos(title, 0, callFunction=callFunction)

    def clearAllSpinBoxes(self, callFunction=False):
        for sb in self.widgetManager.group(WIDGET_NAMES.SpinBox):
            self.setSpinBoxPos(sb, 0, callFunction=callFunction)

    def setSpinBoxPos(self, title, pos, callFunction=True):
        spin = self.widgetManager.get(WIDGET_NAMES.SpinBox, title)
        vals = spin.cget("values")  # .split()
        vals = self._getSpinBoxValsAsList(vals)
        pos = int(pos)
        if pos < 0 or pos >= len(vals):
            raise Exception( "Invalid position: " + str(pos) + ". No position in SpinBox: " +
                        title + "=" + str(vals))
        pos = len(vals) - 1 - pos
        val = vals[pos]
        self._setSpinBoxVal(spin, val, callFunction)

    def changeSpinBox(self, title, vals, reverse=True):
        spin = self.widgetManager.get(WIDGET_NAMES.SpinBox, title)
        if spin.isRange:
            self.warn("Can't convert %s RangeSpinBox to SpinBox", title)
        else:
            self._populateSpinBox(spin, vals, reverse)
            self.setSpinBoxPos(title, 0)

#####################################
# FUNCTION to add images
#####################################

    def image(self, title, value=None, *args, **kwargs):
        """ simpleGUI - adds, sets & gets images all in one go """

        widgKind = WIDGET_NAMES.Image
        kind = kwargs.pop("kind", "standard").lower().strip()
        speed = kwargs.pop("speed", None)
        drop = kwargs.pop("drop", None)
        over = kwargs.pop("over", None)
        submit = kwargs.pop("submit", None)
        _map = kwargs.pop("map", None)

        try: self.widgetManager.verify(widgKind, title)
        except: # already exists
            if value is not None:
                if kind == "data":
                    self.setImageData(title, value, **kwargs)
                elif kind == "icon":
                    gui.warn("Changing image icons not yet supported: %s.", title)
                else:
                    self.setImage(title, value)
            image =  self.getImage(title)
        else: # new widget
            kwargs = self._parsePos(kwargs.pop("pos", []), kwargs)
            if kind == "icon":
                image = self.addIcon(title, value, *args, **kwargs)
            elif kind == "data":
                image = self.addImageData(title, value, *args, **kwargs)
            else:
                image = self.addImage(title, value, *args, **kwargs)


        if speed is not None: self.setAnimationSpeed(title, speed)
        if over is not None: self.setImageMouseOver(title, over)
        if submit is not None:
            if _map is not None: self.setImageMap(title, submit, _map)
            else: self.setImageSubmitFunction(title, submit)
        elif submit is None and _map is not None:
            gui.warn("Must specify a submit function when setting an image map: %s", title)
        if drop is not None: self.setImageDropTarget(title, drop)

        if len(kwargs) > 0:
            self._configWidget(title, widgKind, **kwargs)

        return image

    # looks up label containing image
    def _animateImage(self, title, firstTime=False):
        if not self.alive: return
        try:
            lab = self.widgetManager.get(WIDGET_NAMES.Image, title)
        except ItemLookupError:
            # image destroyed...
            try: self.widgetManager.remove(WIDGET_NAMES.AnimationID, title)
            except: pass
            return
        if not lab.image.animating:
            self.widgetManager.remove(WIDGET_NAMES.AnimationID, title)
            return
        if firstTime and lab.image.alreadyAnimated:
            return

        lab.image.alreadyAnimated = True
        try:
            if lab.image.cached:
                pic = lab.image.pics[lab.image.anim_pos]
            else:
                pic = PhotoImage(file=lab.image.path,
                                 format="gif - {0}".format(lab.image.anim_pos))
                lab.image.pics.append(pic)
            lab.image.anim_pos += 1
            lab.config(image=pic)
            anim_id = self.topLevel.after(int(lab.image.anim_speed), self._animateImage, title)
            self.widgetManager.update(WIDGET_NAMES.AnimationID, title, anim_id)
        except IndexError:
            # will be thrown when we reach end of anim images
            lab.image.anim_pos = 0
            lab.image.cached = True
            self._animateImage(title)
        except TclError:
            # will be thrown when all images cached
            lab.image.anim_pos = 0
            lab.image.cached = True
            self._animateImage(title)

    def _preloadAnimatedImage(self, img):
        if not self.alive: return
        if img.cached:
            return
        try:
            pic = PhotoImage(file=img.path,
                             format="gif - {0}".format(img.anim_pos))
            img.pics.append(pic)
            img.anim_pos += 1
            self.preloadAnimatedImageId = self.topLevel.after(
                0, self._preloadAnimatedImage, img)
        # when all frames have been processed
        except TclError as e:
            # expected - when all images cached
            img.anim_pos = 0
            img.cached = True

    def _configAnimatedImage(self, img):
        img.alreadyAnimated = False
        img.isAnimated = True
        img.pics = []
        img.cached = False
        img.anim_pos = 0
        img.anim_speed = 150
        img.animating = True

    # simple way to check if image is animated
    def _checkIsAnimated(self, name):
        if imghdr.what(name) == "gif":
            try:
                PhotoImage(file=name, format="gif - 1")
                return True
            except:
                pass
        return False

    def setAnimationSpeed(self, name, speed):
        img = self.widgetManager.get(WIDGET_NAMES.Image, name).image
        if speed < 1:
            speed = 1
            self.warn("Setting %s speed to 1. Minimum animation speed is 1.", name)
        img.anim_speed = int(speed)

    def stopAnimation(self, name):
        img = self.widgetManager.get(WIDGET_NAMES.Image, name).image
        img.animating = False

    def startAnimation(self, name):
        img = self.widgetManager.get(WIDGET_NAMES.Image, name).image
        if not img.animating:
            img.animating = True
            anim_id = self.topLevel.after(img.anim_speed, self._animateImage, name)
            self.widgetManager.update(WIDGET_NAMES.AnimationID, name, anim_id)

    # function to set an alternative image, when a mouse goes over
    def setImageMouseOver(self, title, overImg):
        lab = self.widgetManager.get(WIDGET_NAMES.Image, title)

        # first check over image & cache it
        fullPath = self.getImagePath(overImg)
        self.topLevel.after(0, self._getImage, fullPath)

        leaveImg = lab.image.path
        lab.bind("<Leave>", lambda e: self.setImage(title, leaveImg, True))
        lab.bind("<Enter>", lambda e: self.setImage(title, fullPath, True))
        lab.hasMouseOver = True

    # function to set an image location
    def setImageLocation(self, location):
        if os.path.isdir(location):
            self.userImages = location
        else:
            raise Exception("Invalid image location: " + location)

    # get the full path of an image (including image folder)
    def getImagePath(self, imagePath):
        if imagePath is None:
            return None

        if self.userImages is not None:
            imagePath = os.path.join(self.userImages, imagePath)

        absPath = os.path.abspath(imagePath)
        return absPath

    # function to see if an image has changed
    def hasImageChanged(self, originalImage, newImage):
        newAbsImage = self.getImagePath(newImage)

        if originalImage is None:
            return True

        # filename has changed
        if originalImage.path != newAbsImage:
            return True

        # modification time has changed
        if originalImage.modTime != os.path.getmtime(newAbsImage):
            return True

        # no changes
        return False

    # function to remove image objects form cache
    def clearImageCache(self):
        self.widgetManager.clear(WIDGET_NAMES.ImageCache)

    # internal function to build an image function from a string
    def _getImageData(self, imageData, fmt="gif"):
        if fmt=="png":
            self._importPngimagetk()
            if PngImageTk is False:
                raise Exception("TKINTERPNG library not found, PNG files not supported: imageData")
            if sys.version_info >= (2, 7):
                self.warn("Image processing for .PNGs is slow. .GIF is the recommended format")
#                png = PngImageTk(imagePath)
#                png.convert()
#                photo = png.image
            else:
                raise Exception("PNG images only supported in python 3: imageData")

        elif fmt == "gif":
            imgObj = PhotoImage(data=imageData)

        else:
            # expect we already have a PhotoImage object, for example created by PIL
            imgObj = imageData


        imgObj.path = None
        imgObj.modTime = datetime.datetime.now()
        imgObj.isAnimated = False
        imgObj.animating = False
        return imgObj

    # internal function to check/build image object
    def _getImage(self, imagePath, checkCache=True, addToCache=True):
        if imagePath is None:
            return None

        # get the full image path
        imagePath = self.getImagePath(imagePath)

        # if we're caching, and we have a non-None entry in the cache - get it...
        photo = None
        if checkCache and imagePath in self.widgetManager.group(WIDGET_NAMES.ImageCache) and self.widgetManager.get(WIDGET_NAMES.ImageCache, imagePath) is not None:
            photo = self.widgetManager.get(WIDGET_NAMES.ImageCache, imagePath)

        # if the image hasn't changed, use the cache
        if not self.hasImageChanged(photo, imagePath):
            pass
        # else load a new one
        elif os.path.isfile(imagePath):
            if os.access(imagePath, os.R_OK):
                imgType = imghdr.what(imagePath)
                if imgType is None:
                    raise Exception( "Invalid file: " + imagePath + " is not a valid image")
                elif not imagePath.lower().endswith(imgType) and not (
                        imgType == "jpeg" and imagePath.lower().endswith("jpg")):
                        # the image has been saved with the wrong extension
                    raise Exception(
                        "Invalid image extension: " +
                        imagePath +
                        " should be a ." +
                        imgType)
                elif imagePath.lower().endswith('.gif'):
                    photo = PhotoImage(file=imagePath)
                elif imagePath.lower().endswith('.ppm') or imagePath.lower().endswith('.pgm'):
                    photo = PhotoImage(file=imagePath)
                elif imagePath.lower().endswith('jpg') or imagePath.lower().endswith('jpeg'):
                    self.warn("Image processing for .JPGs is slow. .GIF is the recommended format")
                    photo = self.convertJpgToBmp(imagePath)
                elif imagePath.lower().endswith('.png'):
                    # known issue here, some PNGs lack IDAT chunks
                    # also, PNGs seem broken on python<3, maybe around the map
                    # function used to generate pixel maps
                    self._importPngimagetk()
                    if PngImageTk is False:
                        raise Exception(
                            "TKINTERPNG library not found, PNG files not supported: " + imagePath)
                    if sys.version_info >= (2, 7):
                        self.warn("Image processing for .PNGs is slow. .GIF is the recommended format")
                        png = PngImageTk(imagePath)
                        png.convert()
                        photo = png.image
                    else:
                        raise Exception("PNG images only supported in python 3: " + imagePath)
                else:
                    raise Exception("Invalid image type: " + imagePath)
            else:
                raise Exception("Can't read image: " + imagePath)
        else:
            raise Exception("Image " + imagePath + " does not exist")

        # store the full path to this image
        photo.path = imagePath
        # store the modification time
        photo.modTime = os.path.getmtime(imagePath)

        # sort out if it's an animated image
        if self._checkIsAnimated(imagePath):
            self._configAnimatedImage(photo)
            self._preloadAnimatedImage(photo)
        else:
            photo.isAnimated = False
            photo.animating = False
            if addToCache:
                self.widgetManager.update(WIDGET_NAMES.ImageCache, imagePath, photo)

        return photo

    def getImageDimensions(self, name):
        img = self.widgetManager.get(WIDGET_NAMES.Image, name).image
        return img.width(), img.height()

    # force replace the current image, with a new one
    def reloadImage(self, name, imageFile):
        label = self.widgetManager.get(WIDGET_NAMES.Image, name)
        image = self._getImage(imageFile, False)
        self._populateImage(name, image)

    def reloadImageData(self, name, imageData, fmt="gif"):
        self.setImageData(name, imageData, fmt)

    def setImageData(self, name, imageData, fmt="gif"):
        label = self.widgetManager.get(WIDGET_NAMES.Image, name)
        image = self._getImageData(imageData, fmt=fmt)
        self._populateImage(name, image)

    # replace the current image, with a new one
    def getImage(self, name):
        label = self.widgetManager.get(WIDGET_NAMES.Image, name)
        return label.image.path

    def setImage(self, name, imageFile, internal=False):
        label = self.widgetManager.get(WIDGET_NAMES.Image, name)
        imageFile = self.getImagePath(imageFile)

        # only set the image if it's different
        if label.image is not None and label.image.path == imageFile:
            self.warn("Not updating %s, %s hasn't changed." , name, imageFile)
            return
        elif imageFile is None:
            return
        else:
            image = self._getImage(imageFile)
            self._populateImage(name, image, internal)

    # internal function to update the image in a label
    def _populateImage(self, name, image, internal=False):
        label = self.widgetManager.get(WIDGET_NAMES.Image, name)

        if label.image is not None: label.image.animating = False
        label.config(image=image)
        label.config(anchor=CENTER, font=self._getContainerProperty('labelFont'))
        if not self.ttkFlag:
            label.config(background=self._getContainerBg())
        label.image = image  # keep a reference!

        if image.isAnimated:
            anim_id = self.topLevel.after(
                image.anim_speed + 100,
                self._animateImage,
                name,
                True)
            self.widgetManager.update(WIDGET_NAMES.AnimationID, name, anim_id)

        if not internal and label.hasMouseOver:
            leaveImg = label.image.path
            label.bind("<Leave>", lambda e: self.setImage(name, leaveImg, True))

        # removed - keep the label the same size, and crop images
        #h = image.height()
        #w = image.width()
        #label.config(height=h, width=w)
        self.topLevel.update_idletasks()

    # function to configure an image map
    def setImageMap(self, name, func, coords):
        self._setWidgetMap(name, WIDGET_NAMES.Image, func, coords)

    def _setWidgetMap(self, name, _type, func, coords):
        widget = self.widgetManager.get(_type, name)
        rectangles = []
        if len(coords) > 0:
            for k, v in coords.items():
                rect = AjRectangle(k, AjPoint(v[0], v[1]), v[2]-v[0], v[3]-v[1])
                rectangles.append(rect)

        widget.MAP_COORDS = rectangles
        widget.MAP_FUNC = func
        widget.bind("<Button-1>", lambda e: self._widgetMap(_type, name, e), add="+")

    # function called when an image map is clicked
    def _widgetMap(self, _type, name, event):
        widget = self.widgetManager.get(_type, name)
        for rect in widget.MAP_COORDS:
            if rect.contains(AjPoint(event.x, event.y)):
                widget.MAP_FUNC(rect.name)
                return

        widget.MAP_FUNC("UNKNOWN: " + str(event.x) + ", " + str(event.y))

    def addImage(self, name, imageFile, row=None, column=0, colspan=0, rowspan=0, compound=None):
        ''' Adds an image at the specified position '''
        self.widgetManager.verify(WIDGET_NAMES.Image, name)
        imgObj = self._getImage(imageFile)
        self._addImageObj(name, imgObj, row, column, colspan, rowspan, compound=compound)
        self.widgetManager.get(WIDGET_NAMES.Image, name).hasMouseOver = False
        return imgObj

    def addIcon(self, name, iconName, row=None, column=0, colspan=0, rowspan=0, compound=None):
        ''' adds one of the built-in  icons at the specified position '''
        icon = os.path.join(self.icon_path, iconName.lower()+".png")
        with PauseLogger():
            return self.addImage(name, icon, row, column, colspan, rowspan, compound=compound)

    def addImageData(self, name, imageData, row=None, column=0, colspan=0, rowspan=0, fmt="gif", compound=None):
        ''' load image from base-64 encoded GIF
            use base64 module to convert binary data to base64 '''
        self.widgetManager.verify(WIDGET_NAMES.Image, name)
        imgObj = self._getImageData(imageData, fmt)
        self._addImageObj(name, imgObj, row, column, colspan, rowspan, compound=compound)
        self.widgetManager.get(WIDGET_NAMES.Image, name).hasMouseOver = False
        return imgObj

    def _addImageObj(self, name, img, row=None, column=0, colspan=0, rowspan=0, compound=None):
        if not self.ttkFlag:
            label = Label(self.getContainer())
            label.config(background=self._getContainerBg())
        else:
            label = ttk.Label(self.getContainer())

        label.config(anchor=CENTER, font=self._getContainerProperty('labelFont'),image=img)
        label.image = img  # keep a reference!

        if compound is not None:
            label.config(text=name, compound=compound)

        if img is not None and compound is None and not self.ttkFlag:
            h = img.height()
            w = img.width()
            label.config(height=h, width=w)

        self.widgetManager.add(WIDGET_NAMES.Image, name, label)
        self._positionWidget(label, row, column, colspan, rowspan)
        if img is not None and img.isAnimated:
            anim_id = self.topLevel.after(
                img.anim_speed, self._animateImage, name, True)
            self.widgetManager.update(WIDGET_NAMES.AnimationID, name, anim_id)

    def setImageSize(self, name, width, height):
        img = self.widgetManager.get(WIDGET_NAMES.Image, name)
        img.config(height=height, width=width)

#      def rotateImage(self, name, image):
#            img = self.widgetManager.get(WIDGET_NAMES.Image, name)

    # if +ve then grow, else shrink...
    def zoomImage(self, name, x, y=''):
        if x <= 0:
            self.shrinkImage(name, x * -1, y * -1)
        else:
            self.growImage(name, x, y)

    # get every nth pixel (must be an integer)
    # 0 will return an empty image, 1 will return the image, 2 will be 1/2 the
    # size ...
    def shrinkImage(self, name, x, y=''):
        label = self.widgetManager.get(WIDGET_NAMES.Image, name)
        image = label.image.subsample(x, y)

        label.config(image=image)
        label.config(anchor=CENTER, font=self._getContainerProperty('labelFont'))

        if not self.ttkFlag:
            label.config(background=self._getContainerBg())
            label.config(width=image.width(), height=image.height())
        label.modImage = image  # keep a reference!

    # get every nth pixel (must be an integer)
    # 0 won't work, 1 will return the original size
    def growImage(self, name, x, y=''):
        label = self.widgetManager.get(WIDGET_NAMES.Image, name)
        image = label.image.zoom(x, y)

        label.config(image=image)
        label.config(anchor=CENTER, font=self._getContainerProperty('labelFont'))

        if not self.ttkFlag:
            label.config(background=self._getContainerBg())
            label.config(width=image.width(), height=image.height())
        label.modImage = image  # keep a reference!

    def convertJpgToBmp(self, image):
        self._loadNanojpeg()
        if nanojpeg is False:
            raise Exception(
                "nanojpeg library not found, unable to display jpeg files: " + image)
        elif sys.version_info < (2, 7):
            raise Exception(
                "JPG images only supported in python 2.7+: " + image)
        else:
            # read the image into an array of bytes
            with open(image, 'rb') as inFile:
                buf = array.array(str('B'), inFile.read())

            # init the translator, and decode the array of bytes
            nanojpeg.njInit()
            nanojpeg.njDecode(buf, len(buf))

            # determine a file name & type
            if nanojpeg.njIsColor():
#                fileName = image.split('.jpg', 1)[0] + '.ppm'
                param = 6
            else:
#                fileName = image.split('.jpg', 1)[0] + '.pgm'
#                fileName = "test3.pgm"
                param = 5

            # create a string, starting with the header
            val = "P%d\n%d %d\n255\n" % (
                param, nanojpeg.njGetWidth(), nanojpeg.njGetHeight())
            # append the bytes, converted to chars
            val = str(val) + str('').join(map(chr, nanojpeg.njGetImage()))

            # release any stuff
            nanojpeg.njDone()

            photo = PhotoImage(data=val)
            return photo

            # write the chars to a new file, if python3 we need to encode them first
#            with open(fileName, "wb") as outFile:
#                  if sys.version_info[0] == 2: outFile.write(val)
#                  else: outFile.write(val.encode('ISO-8859-1'))
#
#            return fileName

    # function to set a background image
    # make sure this is done before everything else, otherwise it will cover
    # other widgets
    def setBgImage(self, image):
        image = self._getImage(image, False, False)  # make sure it's not using the cache
        # self.containerStack[0]['container'].config(image=image) # window as a
        # label doesn't work...
        self.bgLabel.config(image=image)
        self.containerStack[0]['container'].image = image  # keep a reference!

    def removeBgImage(self):
        self.bgLabel.config(image="")
        # self.containerStack[0]['container'].config(image=None) # window as a
        # label doesn't work...
        # remove the reference - shouldn't be cached
        self.containerStack[0]['container'].image = None

    def resizeBgImage(self):
        if self.containerStack[0]['container'].image is None:
            return
        else:
            pass

#####################################
# FUNCTION to play sounds
#####################################

    # function to set a sound location
    def setSoundLocation(self, location):
        if os.path.isdir(location):
            self.userSounds = location
        else:
            raise Exception("Invalid sound location: " + location)

    # internal function to manage sound availability
    def _soundWrap(self, sound, isFile=False, repeat=False, wait=False):
        self._loadWinsound()
        if self.platform == self.WINDOWS and winsound is not False:
            sound = self._translateSound(sound)
            if self.userSounds is not None and sound is not None:
                sound = os.path.join(self.userSounds, sound)
            if isFile:
                if os.path.isfile(sound) is False:
                    raise Exception("Can't find sound: " + sound)
                if not sound.lower().endswith('.wav'):
                    raise Exception("Invalid sound format: " + sound)
                kind = winsound.SND_FILENAME
                if not wait:
                    kind = kind | winsound.SND_ASYNC
            else:
                if sound is None:
                    kind = winsound.SND_FILENAME
                else:
                    kind = winsound.SND_ALIAS
                    if not wait:
                        kind = kind | winsound.SND_ASYNC

            if repeat:
                kind = kind | winsound.SND_LOOP

            winsound.PlaySound(sound, kind)
        else:
            # sound not available at this time
            raise Exception(
                "Sound not supported on this platform: " +
                platform())

    def playSound(self, sound, wait=False):
        self._soundWrap(sound, True, False, wait)

    def stopSound(self):
        self._soundWrap(None)

    def loopSound(self, sound):
        self._soundWrap(sound, True, True)

    def soundError(self):
        self._soundWrap("SystemHand")

    def soundWarning(self):
        self._soundWrap("SystemAsterisk")

    def bell(self):
        self.containerStack[0]['container'].bell()

    def playNote(self, note, duration=200):
        self._loadWinsound()
        if self.platform == self.WINDOWS and winsound is not False:
            try:
                if isinstance(note, UNIVERSAL_STRING):
                    freq = self.NOTES[note.lower()]
                else:
                    freq = note
            except KeyError:
                raise Exception("Error: cannot play note - " + note)
            try:
                if isinstance(duration, UNIVERSAL_STRING):
                    length = self.DURATIONS[duration.upper()]
                else:
                    length = duration
            except KeyError:
                raise Exception("Error: cannot play duration - " + duration)

            try:
                winsound.Beep(freq, length)
            except RuntimeError:
                raise Exception(
                    "Sound not available on this platform: " +
                    platform())
        else:
            # sound not available at this time
            raise Exception(
                "Sound not supported on this platform: " +
                platform())

#####################################
# FUNCTION for radio buttons
#####################################

    def radio(self, title, name=None, *args, **kwargs):
        """ simpleGUI - shortner for radioButton() """
        return self.radioButton(title, name, *args, **kwargs)

    def radioButton(self, title, name=None, *args, **kwargs):
        """ simpleGUI - adds, sets & gets radioButtons all in one go """
        widgKind = WIDGET_NAMES.RadioButton
        selected = kwargs.pop("selected", False)
        callFunction = kwargs.pop("callFunction", True)
        change = kwargs.pop("change", None)
        kind = kwargs.pop('kind', 'standard')

        # need slightly different approach, as use two params
        if name is None: return self.getRadioButton(title) # no name = get
        else:
            ident = title + "-" + name
            try: self.widgetManager.verify(widgKind, ident)
            except:
                self.setRadioButton(title, name, callFunction=callFunction)
                rb = self.getRadioButton(title)
                selected = False
            else:
                kwargs = self._parsePos(kwargs.pop("pos", []), kwargs)
                rb = self._radioButtonMaker(title, name, *args, **kwargs)

            if selected: self.setRadioButton(title, name)
            if change is not None: self.setRadioButtonChangeFunction(title, change)
            if kind == "square":
                if self.platform == self.MAC:
                    gui.warn("Square radiobuttons not available on Mac, for radiobutton %s", title)
                elif not self.ttkFlag:
                    rb.config(indicatoron=0)
                else:
                    gui.warn("Square radiobuttons not available in ttk, for radiobutton %s", title)

            if len(kwargs) > 0:
                self._configWidget(ident, widgKind, **kwargs)

            return rb

    def _radioButtonMaker(self, title, name, row=None, column=0, colspan=0, rowspan=0, **kwargs):
        return self.addRadioButton(title, name, row, column, colspan, rowspan)

    def addRadioButton(self, title, name, row=None, column=0, colspan=0, rowspan=0):
        ''' adds a radio button, to thr group 'title' with the text 'name' '''
        ident = title + "-" + name
        self.widgetManager.verify(WIDGET_NAMES.RadioButton, ident)

        var = None
        newRb = False
        # title - is the grouper
        # so, if we already have an entry in n_rbVars - get it
        if (title in self.widgetManager.group(WIDGET_NAMES.RadioButton, group=WidgetManager.VARS)):
            var = self.widgetManager.get(WIDGET_NAMES.RadioButton, title, group=WidgetManager.VARS)
        else:
            # if this is a new grouper - set it all up
            var = StringVar(self.topLevel)
            self.widgetManager.add(WIDGET_NAMES.RadioButton, title, var, group=WidgetManager.VARS)
            newRb = True

        # finally, create the actual RadioButton
        if not self.ttkFlag:
            rb = Radiobutton(self.getContainer(), text=name, variable=var, value=name)
            rb.config(anchor=W, background=self._getContainerBg(), indicatoron=1,
                activebackground=self._getContainerBg(), font=self._getContainerProperty('labelFont')
            )
        else:
            rb = ttk.Radiobutton(self.getContainer(), text=name, variable=var, value=name)

        rb.bind("<Button-1>", self._grabFocus)
        rb.DEFAULT_TEXT = name

        self.widgetManager.add(WIDGET_NAMES.RadioButton, ident, rb)
        #rb.bind("<Tab>", self._focusNextWindow)
        #rb.bind("<Shift-Tab>", self._focusLastWindow)

        # and select it, if it's the first item in the list
        if newRb:
            rb.select() if not self.ttkFlag else rb.invoke()
            var.startVal = name # so we can reset it...
        self._positionWidget(rb, row, column, colspan, rowspan, EW)
        return rb

    def getRadioButton(self, title):
        var = self.widgetManager.get(WIDGET_NAMES.RadioButton, title, group=WidgetManager.VARS)
        return var.get()

    def getAllRadioButtons(self):
        rbs = {}
        for k in self.widgetManager.group(WIDGET_NAMES.RadioButton, group=WidgetManager.VARS):
            rbs[k] = self.getRadioButton(k)
        return rbs

    def setRadioButton(self, title, value, callFunction=True):
        ident = title + "-" + value
        self.widgetManager.get(WIDGET_NAMES.RadioButton, ident)

        # now call function
        var = self.widgetManager.get(WIDGET_NAMES.RadioButton, title, group=WidgetManager.VARS)
        with PauseCallFunction(callFunction, var, False):
            var.set(value)

    def clearAllRadioButtons(self, callFunction=False):
        for rb in self.widgetManager.group(WIDGET_NAMES.RadioButton, group=WidgetManager.VARS):
            self.setRadioButton(rb, self.widgetManager.get(WIDGET_NAMES.RadioButton, rb, group=WidgetManager.VARS).startVal, callFunction=callFunction)

    def setRadioTick(self, title, tick=True):
        self.warn("Deprecated function (%s) used for %s -> %s use %s instead", 'setRadioTick', 'radioButton', title, 'setRadioSquare')
        self.setRadioSquare(title, square=tick)

    def setRadioSquare(self, title, square=True):
        if self.platform == self.MAC:
            gui.warn("Square radiobuttons not available on Mac, for radiobutton %s", title)
        elif not self.ttkFlag:
            for k, v in self.widgetManager.group(WIDGET_NAMES.RadioButton).items():
                if k.startswith(title+"-"):
                    if square:
                        v.config(indicatoron=1)
                    else:
                        v.config(indicatoron=0)
        else:
            gui.warn("Square radiobuttons not available in ttk mode, for radiobutton %s", title)

#####################################
# FUNCTION for list box
#####################################

    def listbox(self, title, value=None, *args, **kwargs):
        """ simpleGUI - shortner for listBox() """
        return self.listBox(title, value, *args, **kwargs)

    def listBox(self, title, value=None, *args, **kwargs):
        """ simpleGUI -- adds, sets & gets listBoxes all in one go """
        widgKind = WIDGET_NAMES.ListBox

        rows = kwargs.pop("rows", None)
        multi = kwargs.pop("multi", False)
        group = kwargs.pop("group", False)
        selected = kwargs.pop("selected", None)
        first = kwargs.pop("first", False)
        callFunction = kwargs.pop("callFunction", True)

        # select=select, deselect=??, toggle=??, clear=??, rename=set, replace=update, delete=remove
        if value is None: mode = 'get'
        else: mode = 'select'
        mode = kwargs.pop("mode", mode)

        try: self.widgetManager.verify(widgKind, title)
        except: # widget exists
            if mode == "select":
                if value is not None:
                    if isinstance(value, int):
                        self.selectListItemAtPos(title, value, *args, **kwargs)
                    else:
                        self.selectListItem(title, value, *args, **kwargs)
                else: gui.error("No item specified to select in listbox: %s", title)
            elif mode == "deselect":
                if value is not None:
                    if isinstance(value, int):
                        self.deselectListItemAtPos(title, value, *args, **kwargs)
                    else:
                        self.deselectListItem(title, value, *args, **kwargs)
                else: gui.error("No item specified to deselect in listbox: %s", title)
            elif mode == "toggle":
                gui.error("%s not implemented yet in listbox: %s", mode, title)
            elif mode == "clear":
                self.deselectAllListItems(title)
            elif mode == "rename":
                gui.error("%s not implemented yet in listbox: %s", mode, title)
            elif mode == "replace":
                if value is not None: self.updateListBox(title, items=value, callFunction=callFunction)
                else: gui.error("No values specified to replace in listbox: %s", title)
            elif mode == "delete":
                if value is not None:
                    if isinstance(value, int):
                        self.removeListItemAtPos(title, value)
                    else:
                        self.removeListItem(title, value)
                else: gui.error("No value specified to delete in listbox: %s", title)
            elif mode == "add":
                if value is not None:
                    select = True if selected is None else selected
                    if type(value) in (list, tuple):
                        self.addListItems(title, items=value, select=select)
                    else:
                        self.addListItem(title, item=value, select=select)
                else: gui.error("No value specified to add in listbox: %s", title)
            elif mode == "get":
                pass
            else:
                gui.error("Invalid mode (%s) specified in listbox: %s", mode, title)

            listBox = self.getListBox(title)
        else: # new widget
            kwargs = self._parsePos(kwargs.pop("pos", []), kwargs)
            listBox = self._listBoxMaker(title, value, *args, **kwargs)

        if rows is not None: self.setListBoxRows(title, rows)
        if multi: self.setListBoxMulti(title)
        if group: self.setListBoxGroup(title)
        if selected is not None: self.selectListItemAtPos(title, selected, callFunction=False)

        if len(kwargs) > 0:
            self._configWidget(title, widgKind, **kwargs)

        return listBox

    def _listBoxMaker(self, name, values=None, row=None, column=0, colspan=0, rowspan=0, **kwargs):
        """ internal wrapper to hide kwargs from original add functions """
        return self.addListBox(name, values, row, column, colspan, rowspan)

    def addListBox(self, name, values=None, row=None, column=0, colspan=0, rowspan=0):
        ''' adds a list box, with the the specified list of values '''
        self.widgetManager.verify(WIDGET_NAMES.ListBox, name)
        container = self.makeListBoxContainer()(self.getContainer())
        vscrollbar = AutoScrollbar(container)
        hscrollbar = AutoScrollbar(container, orient=HORIZONTAL)

        container.lb = Listbox(container,
            yscrollcommand=vscrollbar.set,
            xscrollcommand=hscrollbar.set)

        vscrollbar.grid(row=0, column=1, sticky=N + S)
        hscrollbar.grid(row=1, column=0, sticky=E + W)

        container.lb.grid(row=0, column=0, sticky=N + S + E + W)

        container.grid_rowconfigure(0, weight=1)
        container.grid_columnconfigure(0, weight=1)

        vscrollbar.config(command=container.lb.yview)
        hscrollbar.config(command=container.lb.xview)

        container.lb.config(font=self._getContainerProperty('inputFont'))
        self.widgetManager.add(WIDGET_NAMES.ListBox, name, container.lb)

        container.lb.DEFAULT_TEXT=""
        if values is not None:
            container.lb.DEFAULT_TEXT='\n'.join(str(x) for x in values)
            for name in values:
                container.lb.insert(END, name)

        self._positionWidget(container, row, column, colspan, rowspan)
        return container.lb

    # enable multiple listboxes to be selected at the same time
    def setListBoxGroup(self, name, group=True):
        lb = self.widgetManager.get(WIDGET_NAMES.ListBox, name)
        group = not group
        lb.config(exportselection=group)

    # set how many rows to display
    def setListBoxRows(self, name, rows):
        lb = self.widgetManager.get(WIDGET_NAMES.ListBox, name)
        lb.config(height=rows)

    # make the list single/multi select
    # default is single
    def setListBoxMulti(self, title, multi=True):
        lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title)
        if multi:
            lb.config(selectmode=EXTENDED)
        else:
            lb.config(selectmode=BROWSE)

    # select the specified item in the list
    def selectListItem(self, title, item, callFunction=True):
        lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title)
        positions = self._getListPositions(title, item)
        if len(positions) > 1 and lb.cget("selectmode") == EXTENDED:
            allOk = True
            for pos in positions:
                if not self.selectListItemAtPos(title, pos, callFunction):
                    allOk = False
            return allOk
        elif len(positions) > 1:
            gui.warn("Unable to select multiple items for list: %s. Selecting first item: %s", title, item[0])
            return self.selectListItemAtPos(title, positions[0], callFunction)
        elif len(positions) == 1:
            return self.selectListItemAtPos(title, positions[0], callFunction)
        else:
            gui.warn("Invalid list item(s): %s for list: %s", item, title)
            return False

    def deselectListItemAtPos(self, title, pos, callFunction=False):
        lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title)
        if lb.size() == 0:
            gui.warn("No items in list: %s, unable to deselect item at pos: %s", title, pos)
            return False
        if pos < 0 or pos > lb.size() - 1:
            gui.warn("Invalid list position: %s for list: %s (max: %s)", pos, title, lb.size()-1)
            return False

        lb.selection_clear(pos)
        if callFunction and hasattr(lb, 'cmd'):
            lb.cmd()
        self.topLevel.update_idletasks()
        return True

    def selectListItemAtPos(self, title, pos, callFunction=False):
        lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title)
        if lb.size() == 0:
            gui.warn("No items in list: %s, unable to select item at pos: %s", title, pos)
            return False
        if pos < 0 or pos > lb.size() - 1:
            gui.warn("Invalid list position: %s for list: %s (max: %s)", pos, title, lb.size()-1)
            return False

        # clear previous selection if we're not multi
        if lb.cget("selectmode") != EXTENDED:
            lb.selection_clear(0, END)

        # show & select this item
        lb.see(pos)
        lb.activate(pos)
        lb.selection_set(pos)
        # now call function
        if callFunction and hasattr(lb, 'cmd'):
            lb.cmd()
        self.topLevel.update_idletasks()
        return True

    # replace the list items in the list box
    def updateListBox(self, title, items, select=False, callFunction=True):
        self.clearListBox(title, callFunction=callFunction)
        self.addListItems(title, items, select=select)

    def addListItems(self, title, items, select=True):
        ''' adds the list of items to the specified list box '''
        for i in items:
            self.addListItem(title, i, select=select)

    def addListItem(self, title, item, pos=None, select=True):
        ''' add the item to the end of the specified list box '''
        lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title)
        # add it at the end
        if pos is None: pos = END
        lb.insert(pos, item)

        # show & select the newly added item
        if select:
            # clear any selection
            items = lb.curselection()
            if len(items) > 0:
                lb.selection_clear(items)

            self.selectListItemAtPos(title, lb.size() - 1)

    def deselectAllListItems(self, title, callFunction=False):
        lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title)
        lb.selection_clear(0, END)

        if callFunction and hasattr(lb, 'cmd'):
            lb.cmd()

    # returns a list containing 0 or more elements
    # all that are in the selected range
    def getListBox(self, title):
        lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title)
        items = lb.curselection()
        values = []
        for loop in range(len(items)):
            values.append(lb.get(items[loop]))
        return values

    def getAllListBoxes(self):
        boxes = {}
        for k in self.widgetManager.group(WIDGET_NAMES.ListBox):
            boxes[k] = self.getListBox(k)
        return boxes

    def getAllListItems(self, title):
        lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title)
        items = lb.get(0, END)
        return list(items)

    def getListBoxPos(self, title):
        lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title)
        # bug in tkinter 1.160 returns these as strings
        items = [int(i) for i in lb.curselection()]

        return items

    def removeListItemAtPos(self, title, pos):
        lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title)
        items = lb.get(0, END)
        if pos >= len(items):
            raise Exception("Invalid position: " + str(pos) + " must be between 0 and " + str(len(items)-1))
        lb.delete(pos)

        # show & select this item
        if pos >= lb.size():
            pos -= 1
        self.selectListItemAtPos(title, pos)

    # remove a specific item from the listBox
    # will only remove the first item that matches the String
    def removeListItem(self, title, item):
        lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title)
        positions = self._getListPositions(title, item)
        if len(positions) > 0:
            lb.delete(positions[0])

        # show & select this item
        if positions[0] >= lb.size():
            positions[0] -= 1
        self.selectListItemAtPos(title, positions[0])

    def setListItemAtPos(self, title, pos, newVal):
        lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title)
        lb.delete(pos)
        lb.insert(pos, newVal)

    def setListItem(self, title, item, newVal, first=False):
        for pos in self._getListPositions(title, item):
            self.setListItemAtPos(title, pos, newVal)
            if first:
                break

    # functions to config
    def setListItemAtPosBg(self, title, pos, col):
        lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title)
        lb.itemconfig(pos, bg=col)

    def setListItemAtPosFg(self, title, pos, col):
        lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title)
        lb.itemconfig(pos, fg=col)

    def _getListPositions(self, title, item):
        lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title)
        if not isinstance(item, list):
            item = [item]
        vals = lb.get(0, END)
        positions = []
        for pos, val in enumerate(vals):
            if val in item:
                positions.append(pos)
        return positions

    def setListItemBg(self, title, item, col):
        for pos in self._getListPositions(title, item):
            self.setListItemAtPosBg(title, pos, col)

    def setListItemFg(self, title, item, col):
        for pos in self._getListPositions(title, item):
            self.setListItemAtPosFg(title, pos, col)

    def clearListBox(self, title, callFunction=True):
        lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title)
        lb.selection_clear(0, END)
        lb.delete(0, END)  # clear
        if callFunction and hasattr(lb, 'cmd'):
            lb.cmd()

    def clearAllListBoxes(self, callFunction=False):
        for lb in self.widgetManager.group(WIDGET_NAMES.ListBox):
            self.clearListBox(lb, callFunction)

#####################################
# FUNCTION for buttons
#####################################

    def button(self, title, value=None, *args, **kwargs):
        """ simpleGUI - adds, sets & gets buttons all in one go """
        widgKind = WIDGET_NAMES.Button
        image = kwargs.pop("image", None)
        icon = kwargs.pop("icon", None)
        name = kwargs.pop("label", kwargs.pop("name", None))

        try: self.widgetManager.verify(WIDGET_NAMES.Button, title)
        except: # widget exists
            if value is not None: self.setButton(title, value)
            button = self.getButton(title)
        else: # new widget
            kwargs = self._parsePos(kwargs.pop("pos", []), kwargs)
            if image is not None: button = self._buttonMaker(title, value, "image", image, *args, **kwargs)
            elif icon is not None: button = self._buttonMaker(title, value, "icon", icon, *args, **kwargs)
            elif name is not None: button = self._buttonMaker(title, value, "named", name, *args, **kwargs)
            else: button = self._buttonMaker(title, value, "button", None, *args, **kwargs)

        if len(kwargs) > 0:
            self._configWidget(title, widgKind, **kwargs)
        return button

    def _buttonMaker(self, title, func, kind, extra=None, row=None, column=0, colspan=0, rowspan=0, *args, **kwargs):
        """ internal wrapper to hide kwargs from original add functions """
        align = kwargs.pop("align", None)
        if kind == "button": return self.addButton(title, func, row, column, colspan, rowspan)
        elif kind == "named": return self.addNamedButton(extra, title, func, row, column, colspan, rowspan)
        elif kind == "image": return self.addImageButton(title, func, extra, row, column, colspan, rowspan, align=align)
        elif kind == "icon": return self.addIconButton(title, func, extra, row, column, colspan, rowspan, align=align)

    def _configWidget(self, title, kind, **kwargs):
        widget = self.widgetManager.get(kind, title)
        # remove any unwanted keys
        for key in ["row", "column", "colspan", "rowspan", "label", "name"]:
            kwargs.pop(key, None)

        # ignore these for now as well
        for key in ["pad", "inpad"]:
            val = kwargs.pop(key, None)
            if val is not None:
                gui.error("Invalid argument for %s %s - %s:%s", WIDGET_NAMES.name(kind), title, key, val)

        tooltip = kwargs.pop("tip", kwargs.pop("tooltip", None))
        change = kwargs.pop("change", None)
        submit = kwargs.pop("submit", None)
        over = kwargs.pop("over", None)
        drag = kwargs.pop("drag", None)
        drop = kwargs.pop("drop", None)
        right = kwargs.pop("right", None)
        focus = kwargs.pop('focus', False)
        _font = kwargs.pop('font', None)

        if tooltip is not None: self._addTooltip(widget, tooltip, None)
        if focus: widget.focus_set()

        if change is not None: self._bindEvent(kind, title, widget, change, "change", key=None)
        if submit is not None: self._bindEvent(kind, title, widget, submit, "submit", key=None)
        if over is not None: self._bindOverEvent(kind, title, widget, over, None, None)
        if drag is not None: self._bindDragEvent(kind, title, widget, drag, None, None)
        if drop is not None: self._registerExternalDropTarget(title, widget, drop)
        if right is not None: self._bindRightClick(widget, right)

        # allow fonts to be passed in as either a dictionary or a single integer or a font object
        if _font is not None:
            if isinstance(_font, tkFont.Font):
                widget.config(font=_font)
            else:
                if not isinstance(_font, dict): # assume int
                    _font = {"size":_font}
                custFont = tkFont.Font(**_font)
                widget.config(font=custFont)

        # now pass the kwargs to the config function, ignore any baddies
        errorMsg = ""
        while True:
            try: widget.config(**kwargs)
            except TclError as e:
                try:
                    key=str(e).split()[2][2:-1]
                    errorMsg = "".join([errorMsg, key, ":", kwargs.pop(key), ", "])
                except:
                    gui.error("Invalid argument for %s %s: %s", WIDGET_NAMES.name(kind), title, e)
                    break
            else:
                break
        if len(errorMsg) > 0:
            gui.error("Invalid arguments for %s %s - %s", WIDGET_NAMES.name(kind), title, errorMsg)


    def _buildButton(self, title, func, frame, name=None):
        if name is None:
            name = title
        if isinstance(title, list):
            raise Exception("Can't add a button using a list of names: " + str(title) + " - you should use .addButtons()")
        self.widgetManager.verify(WIDGET_NAMES.Button, title)
        if not self.ttkFlag:
            but = Button(frame, text=name)
            but.config(font=self._getContainerProperty('buttonFont'))
            if self.platform in [self.MAC, self.LINUX]:
                but.config(highlightbackground=self._getContainerBg())
        else:
            but = ttk.Button(frame, text=name)

        but.DEFAULT_TEXT = name

        if func is not None:
            command = self.MAKE_FUNC(func, title)
            but.config(command=command)

        #but.bind("<Tab>", self._focusNextWindow)
        #but.bind("<Shift-Tab>", self._focusLastWindow)
        self.widgetManager.add(WIDGET_NAMES.Button, title, but)

        return but

    def addNamedButton(self, name, title, func, row=None, column=0, colspan=0, rowspan=0):
        ''' adds a button, displaying the name as its text '''
        but = self._buildButton(title, func, self.getContainer(), name)
        self._positionWidget(but, row, column, colspan, rowspan, None)
        return but

    def addButton(self, title, func, row=None, column=0, colspan=0, rowspan=0):
        ''' adds a button with the title as its text '''
        but = self._buildButton(title, func, self.getContainer())
        self._positionWidget(but, row, column, colspan, rowspan, None)
        return but

    def addImageButton(self, title, func, imgFile, row=None, column=0, colspan=0, rowspan=0, align=None):
        ''' adds a button, displaying the specified image file '''
        but = self._buildButton(title, func, self.getContainer())
        self._positionWidget(but, row, column, colspan, rowspan, None)
        self.setButtonImage(title, imgFile, align)
        return but

    def addIconButton(self, title, func, iconName, row=None, column=0, colspan=0, rowspan=0, align=None):
        ''' adds a button displaying the specified icon '''
        icon = os.path.join(self.icon_path, iconName.lower()+".png")
        with PauseLogger():
            return self.addImageButton(title, func, icon, row, column, colspan, rowspan, align)

    def setButton(self, name, text):
        but = self.widgetManager.get(WIDGET_NAMES.Button, name)
        try: # try to bind a function
            command = self.MAKE_FUNC(text, name)
            but.config(command=command)
        except: # otherwise change the text
            but.config(text=text)

    def getButton(self, name):
        but = self.widgetManager.get(WIDGET_NAMES.Button, name)
        return but.cget("text")

    def setButtonImage(self, name, imgFile, align=None):
        but = self.widgetManager.get(WIDGET_NAMES.Button, name)
        image = self._getImage(imgFile)
        # works on Mac & Windows :)
        if align == None:
            but.config(image=image, text="")
            if not self.ttk:
                but.config(justify=LEFT, compound=TOP)
            else:
                but.config(compound=CENTER)
        else:
            but.config(image=image, compound=align)
        # but.config(image=image, compound=None, text="") # works on Windows, not Mac

        but.image = image

    # adds a set of buttons, in the row, spannning specified columns
    # pass in a list of names & a list of functions (or a single function to
    # use for all)
    def buttons(self, names, funcs, **kwargs):
        kwargs = self._parsePos(kwargs.pop("pos", []), kwargs)
        self._addButtons(names, funcs, **kwargs)
        kwargs.pop('fill', False)
        if not isinstance(names[0], list):
            names = [names]
        for row in names:
            for title in row:
                self._configWidget(title, WIDGET_NAMES.Button, **kwargs)

    def _addButtons(self, names, funcs, row=None, column=0, colspan=0, rowspan=0, fill=False, **kwargs):
        self.addButtons(names, funcs, row, column, colspan, rowspan, fill)

    def addButtons(self, names, funcs, row=None, column=0, colspan=0, rowspan=0, fill=False):
        ''' adds a 1D/2D list of buttons '''
        if not isinstance(names, list):
            raise Exception(
                "Invalid button: " +
                names +
                ". It must be a list of buttons.")

        singleFunc = self._checkFunc(names, funcs)

        frame = self._makeWidgetBox()(self.getContainer())
        if not self.ttk:
            frame.config(background=self._getContainerBg())

        # make them into a 2D array, if not already
        if not isinstance(names[0], list):
            names = [names]
            # won't be used if single func
            if funcs is not None:
                funcs = [funcs]

        sticky = None
        if fill: sticky=E+W

        for bRow in range(len(names)):
            for i in range(len(names[bRow])):
                t = names[bRow][i]
                if funcs is None:
                    tempFunc = None
                elif singleFunc is None:
                    tempFunc = funcs[bRow][i]
                else:
                    tempFunc = singleFunc
                but = self._buildButton(t, tempFunc, frame)

                but.grid(row=bRow, column=i, sticky=sticky)
                Grid.columnconfigure(frame, i, weight=1)
                Grid.rowconfigure(frame, bRow, weight=1)
                frame.theWidgets.append(but)

        self._positionWidget(frame, row, column, colspan, rowspan)
        self.widgetManager.log(WIDGET_NAMES.FrameBox, frame)

#####################################
# FUNCTIONS for links
#####################################

    def link(self, title, value=None, *args, **kwargs):
        """ simpleGUI - adds, sets & gets links all in one go """
        widgKind = WIDGET_NAMES.Link

        try: self.widgetManager.verify(widgKind, title)
        except: # widget exists
            if value is not None: self.setLink(title, value)
            link = self.getLink(title)
        else: # new widget
            if value is None:
                gui.warn("Can't create link: %s, with no value", title)
                return None
            kwargs = self._parsePos(kwargs.pop("pos", []), kwargs)
            link = self._linkMaker(title, value, *args, **kwargs)

        if len(kwargs) > 0:
            self._configWidget(title, widgKind, **kwargs)
        return link

    def _linkMaker(self, title, value, row=None, column=0, colspan=0, rowspan=0, *args, **kwargs):
        if not callable(value) and not hasattr(value, '__call__'):
            return self.addWebLink(title, value, row, column, colspan, rowspan)
        else:
            return self.addLink(title, value, row, column, colspan, rowspan)

    def _buildLink(self, title):
        self._importWebBrowser()
        if not webbrowser:
            self.error("Unable to load webbrowser - can't create links")
        link = self._makeLink()(self.getContainer(), useTtk=self.ttkFlag)
        link.config(text=title, font=self._linkFont)
        if not self.ttk:
            link.config(background=self._getContainerBg())
        self.widgetManager.add(WIDGET_NAMES.Link, title, link)
        return link

    # launches a browser to the specified page
    def addWebLink(self, title, page, row=None, column=0, colspan=0, rowspan=0):
        ''' adds a hyperlink to the specified web page '''
        link = self._buildLink(title)
        link.registerWebpage(page)
        self._positionWidget(link, row, column, colspan, rowspan)
        return link

    # executes the specified function
    def addLink(self, title, func, row=None, column=0, colspan=0, rowspan=0):
        ''' adds a hyperlink to the specified function '''
        link = self._buildLink(title)
        if func is not None:
            myF = self.MAKE_FUNC(func, title)
            link.registerCallback(myF)
        self._positionWidget(link, row, column, colspan, rowspan)
        return link

    def getLink(self, title):
        link = self.widgetManager.get(WIDGET_NAMES.Link, title)
        return link.cget("text")

    def setLink(self, title, func):
        link = self.widgetManager.get(WIDGET_NAMES.Link, title)
        if not callable(func) and not hasattr(func, '__call__'):
            link.registerWebpage(func)
        else:
            myF = self.MAKE_FUNC(func, title)
            link.registerCallback(myF)

#####################################
# FUNCTIONS for grips
#####################################

    def grip(self, *args, **kwargs):
        """ simpleGUI - adds grip """
        kwargs = self._parsePos(kwargs.pop("pos", []), kwargs)
        return self.addGrip(*args, **kwargs)

    # adds a simple grip, used to drag the window around
    def addGrip(self, row=None, column=0, colspan=0, rowspan=0):
        ''' adds a grip, for dragging the GUI around '''
        grip = self._makeGrip()(self.getContainer())
        self._positionWidget(grip, row, column, colspan, rowspan)
        self._addTooltip(grip, "Drag here to move", True)
        return grip


#####################################
# FUNCTIONS for dnd
#####################################

    def addTrashBin(self, title, row=None, column=0, colspan=0, rowspan=0):
        ''' NOT IN USE - adds a trashbin, for discarding dragged items '''
        trash = TrashBin(self.getContainer())
        self._positionWidget(trash, row, column, colspan, rowspan)
        return trash

#####################################
# FUNCTIONS for turtle
#####################################

    def addTurtle(self, title, row=None, column=0, colspan=0, rowspan=0):
        ''' adds a turtle widget at the specified position '''
        self._loadTurtle()
        if turtle is False:
            raise Exception("Unable to load turtle")
        self.widgetManager.verify(WIDGET_NAMES.Turtle, title)
        canvas = Canvas(self.getContainer())
        canvas.screen = turtle.TurtleScreen(canvas)
        self._positionWidget(canvas, row, column, colspan, rowspan)
        self.widgetManager.add(WIDGET_NAMES.Turtle, title, canvas)
        canvas.turtle = turtle.RawTurtle(canvas.screen)
        return canvas.turtle

    def getTurtleScreen(self, title):
        return self.widgetManager.get(WIDGET_NAMES.Turtle, title).screen

    def getTurtle(self, title):
        return self.widgetManager.get(WIDGET_NAMES.Turtle, title).turtle

#####################################
# FUNCTIONS for canvas
#####################################

    def addCanvas(self, title, row=None, column=0, colspan=0, rowspan=0):
        ''' adds a canvas at the specified position '''
        self.widgetManager.verify(WIDGET_NAMES.Canvas, title)
        canvas = Canvas(self.getContainer())
        canvas.config(bd=0, highlightthickness=0)
        canvas.imageStore = []
        self._positionWidget(canvas, row, column, colspan, rowspan, "news")
        self.widgetManager.add(WIDGET_NAMES.Canvas, title, canvas)
        return canvas

    def getCanvas(self, title):
        return self.widgetManager.get(WIDGET_NAMES.Canvas, title)

    def clearCanvas(self, title):
        self.widgetManager.get(WIDGET_NAMES.Canvas, title).delete("all")

    # function to configure a canvas map
    def setCanvasMap(self, name, func, coords):
        self._setWidgetMap(name, WIDGET_NAMES.Canvas, func, coords)

    def addCanvasCircle(self, title, x, y, diameter, **kwargs):
        ''' adds a circle to the specified canvas '''
        return self.addCanvasOval(title, x, y, diameter, diameter, **kwargs)

    def addCanvasOval(self, title, x, y, xDiam, yDiam, **kwargs):
        ''' adds a oval to the specified canvas '''
        return self.widgetManager.get(WIDGET_NAMES.Canvas, title).create_oval(x, y, x+xDiam, y+yDiam, **kwargs)

    def addCanvasLine(self, title, x, y, x2, y2, **kwargs):
        ''' adds a line to the specified canvas '''
        return self.widgetManager.get(WIDGET_NAMES.Canvas, title).create_line(x, y, x2, y2, **kwargs)

    def addCanvasRectangle(self, title, x, y, w, h, **kwargs):
        ''' adds a rectangle to the specified canvas '''
        return self.widgetManager.get(WIDGET_NAMES.Canvas, title).create_rectangle(x, y, x+w, y+h, **kwargs)

    def addCanvasText(self, title, x, y, text=None, **kwargs):
        ''' adds text to the specified canvas '''
        return self.widgetManager.get(WIDGET_NAMES.Canvas, title).create_text(x, y, text=text, **kwargs)

    def addCanvasImage(self, title, x, y, image=image, **kwargs):
        ''' adds an image to the specified canvas '''
        canv = self.widgetManager.get(WIDGET_NAMES.Canvas, title)
        if isinstance(image, UNIVERSAL_STRING):
            image = self._getImage(image)
        canv.imageStore.append(image)
        return self.widgetManager.get(WIDGET_NAMES.Canvas, title).create_image(x, y, image=image, **kwargs)

    def setCanvasEvent(self, title, item, event, function, add=None):
        canvas = self.widgetManager.get(WIDGET_NAMES.Canvas, title)
        canvas.tag_bind(item, event, function, add)

    def _canvasMaker(self, title, row=None, column=0, colspan=0, rowspan=0, **kwargs):
        return self.addCanvas(title, row, column, rowspan)

    def canvas(self, title, *args, **kwargs):
        """ simpleGUI - adds, sets & gets canases all in one go """
        widgKind = WIDGET_NAMES.Canvas
        submit = kwargs.pop("submit", None)
        _map = kwargs.pop("map", None)

        try: self.widgetManager.verify(widgKind, title)
        except: # widget exists
            # NB. no SETTER
            canvas = self.getCanvas(title)
        else: # new widget
            kwargs = self._parsePos(kwargs.pop("pos", []), kwargs)
            canvas = self._canvasMaker(title, *args, **kwargs)

        if submit is not None and _map is not None:
            self.setCanvasMap(title, submit, _map)
        else:
            gui.warn("Must specify a submit function when setting a canvas map: %s", title)

        if len(kwargs) > 0:
            self._configWidget(title, widgKind, **kwargs)
        self._configWidget(title, widgKind, **kwargs)
        return canvas

#####################################
# FUNCTIONS for Microbits
#####################################

    def microbit(self, title, *args, **kwargs):
        '''simpleGUI - adds, sets & gets microbits all in one go'''
        widgKind = WIDGET_NAMES.MicroBit
        image = kwargs.pop("image", None)
        brightness = kwargs.pop("brightness", None)
        x = kwargs.pop("x", None)
        y = kwargs.pop("y", None)
        clear = kwargs.pop("clear", False)

        try: self.widgetManager.verify(widgKind, title)
        except: # widget exists
            mb = self.getMicroBit(title)
        else: # new widget
            kwargs = self._parsePos(kwargs.pop("pos", []), kwargs)
            mb = self.addMicroBit(title, *args, **kwargs)

        if image is not None: self.setMicroBitImage(title, image)
        if brightness is not None: self.setMicroBitPixel(title, x, y, brightness)
        if clear: self.clearMicroBit(title)

        if len(kwargs) > 0:
            self._configWidget(title, widgKind, **kwargs)

        return mb

    def addMicroBit(self, title, row=None, column=0, colspan=0, rowspan=0):
        ''' adds a simple microbit widget
             used with permission from Ben Goodwin '''
        self.widgetManager.verify(WIDGET_NAMES.MicroBit, title)
        mb = MicroBitSimulator(self.getContainer())
        self._positionWidget(mb, row, column, colspan, rowspan)
        self.widgetManager.add(WIDGET_NAMES.MicroBit, title, mb)
        return mb

    def setMicroBitImage(self, title, image):
        self.widgetManager.get(WIDGET_NAMES.MicroBit, title).show(image)

    def setMicroBitPixel(self, title, x, y, brightness):
        self.widgetManager.get(WIDGET_NAMES.MicroBit, title).set_pixel(x, y, brightness)

    def clearMicroBit(self, title):
        self.widgetManager.get(WIDGET_NAMES.MicroBit, title).clear()

#####################################
# DatePicker Widget - using Form Container
#####################################

    def date(self, title, value=None, *args, **kwargs):
        """ simpleGUI - shortner for datePicker() """
        return self.datePicker(title, value, *args, **kwargs)

    def datePicker(self, title, value=None, *args, **kwargs):
        """ simpleGUI - adds, sets & gets datePickers all in one go """
        widgKind = WIDGET_NAMES.DatePicker
        change = kwargs.pop("change", None)
        toValue = kwargs.pop("toValue", None)

        try: self.widgetManager.verify(widgKind, title)
        except: # widget exists
            dp = self.getDatePicker(title)
        else: # new widget
            kwargs = self._parsePos(kwargs.pop("pos", []), kwargs)
            dp = self.addDatePicker(title, *args, **kwargs)

        if value is not None:
            if toValue is None: self.setDatePicker(title, value)
            else: self.setDatePickerRange(title, startYear=value, endYear=toValue)
        if change is not None: self.setDatePickerChangeFunction(title, change)

        if len(kwargs) > 0:
            self._configWidget(title, widgKind, **kwargs)

        return dp

    def addDatePicker(self, name, row=None, column=0, colspan=0, rowspan=0):
        ''' adds a date picker at the specified position '''
        self.widgetManager.verify(WIDGET_NAMES.DatePicker, name)
        # initial DatePicker has these dates
        days = range(1, 32)
        self.MONTH_NAMES = calendar.month_name[1:]
        years = range(1970, 2021)

        # create a frame, and add the widgets
        frame = self.startFrame(name, row, column, colspan, rowspan)
        self.setExpand("none")
        self.addLabel(name + "_DP_DayLabel", "Day:", 0, 0)
        self.setLabelAlign(name + "_DP_DayLabel", "w")
        self.addOptionBox(name + "_DP_DayOptionBox", days, 0, 1)
        self.addLabel(name + "_DP_MonthLabel", "Month:", 1, 0)
        self.setLabelAlign(name + "_DP_MonthLabel", "w")
        self.addOptionBox(name + "_DP_MonthOptionBox", self.MONTH_NAMES, 1, 1)
        self.addLabel(name + "_DP_YearLabel", "Year:", 2, 0)
        self.setLabelAlign(name + "_DP_YearLabel", "w")
        self.addOptionBox(name + "_DP_YearOptionBox", years, 2, 1)
        self.setOptionBoxChangeFunction(
            name + "_DP_MonthOptionBox",
            self._updateDatePickerDays)
        self.setOptionBoxChangeFunction(
            name + "_DP_YearOptionBox",
            self._updateDatePickerDays)
        self.stopFrame()
        frame.isContainer = False
        self.widgetManager.add(WIDGET_NAMES.DatePicker, name, frame)

    def setDatePickerFg(self, name, fg):
        self.widgetManager.get(WIDGET_NAMES.DatePicker, name)
        self.setLabelFg(name + "_DP_DayLabel", fg)
        self.setLabelFg(name + "_DP_MonthLabel", fg)
        self.setLabelFg(name + "_DP_YearLabel", fg)

    def setDatePickerChangeFunction(self, title, function):
        self.widgetManager.get(WIDGET_NAMES.DatePicker, title)
        cmd = self.MAKE_FUNC(function, title)
        self.setOptionBoxChangeFunction(title + "_DP_DayOptionBox", cmd)
        self.widgetManager.get(WIDGET_NAMES.OptionBox, title + "_DP_DayOptionBox").function = cmd

    # function to update DatePicker dropDowns
    def _updateDatePickerDays(self, title):
        if title.find("_DP_MonthOptionBox") > -1:
            title = title.split("_DP_MonthOptionBox")[0]
        elif title.find("_DP_YearOptionBox") > -1:
            title = title.split("_DP_YearOptionBox")[0]
        else:
            self.warn("Can't update days in DatePicker:%s", title)
            return

        day = self.getOptionBox(title + "_DP_DayOptionBox")
        month = self.MONTH_NAMES.index(self.getOptionBox(title + "_DP_MonthOptionBox")) + 1
        year = int(self.getOptionBox(title + "_DP_YearOptionBox"))
        days = range(1, calendar.monthrange(year, month)[1] + 1)
        self.changeOptionBox(title + "_DP_DayOptionBox", days)

        # keep previous day if possible
        with PauseLogger():
            self.setOptionBox(title + "_DP_DayOptionBox", day, callFunction=False)

        box = self.widgetManager.get(WIDGET_NAMES.OptionBox, title + "_DP_DayOptionBox")
        if hasattr(box, 'function'):
            box.function()

    # set a date for the named DatePicker
    def setDatePickerRange(self, title, startYear, endYear=None):
        self.widgetManager.get(WIDGET_NAMES.DatePicker, title)
        if endYear is None:
            endYear = datetime.date.today().year
        years = range(startYear, endYear + 1)
        self.changeOptionBox(title + "_DP_YearOptionBox", years)

    def setDatePicker(self, title, date="today"):
        self.widgetManager.get(WIDGET_NAMES.DatePicker, title)
        if date == "today":
            date = datetime.date.today()
        self.setOptionBox(title + "_DP_YearOptionBox", str(date.year))
        self.setOptionBox(title + "_DP_MonthOptionBox", date.month - 1)
        self.setOptionBox(title + "_DP_DayOptionBox", date.day - 1)

    def clearDatePicker(self, title, callFunction=True):
        self.widgetManager.get(WIDGET_NAMES.DatePicker, title)
        self.setOptionBox(title + "_DP_YearOptionBox", 0, callFunction)
        self.setOptionBox(title + "_DP_MonthOptionBox", 0, callFunction)
        self.setOptionBox(title + "_DP_DayOptionBox", 0, callFunction)

    def clearAllDatePickers(self, callFunction=False):
        for k in self.widgetManager.group(WIDGET_NAMES.DatePicker):
            self.clearDatePicker(k, callFunction)

    def getDatePicker(self, title):
        self.widgetManager.get(WIDGET_NAMES.DatePicker, title)
        day = int(self.getOptionBox(title + "_DP_DayOptionBox"))
        month = self.MONTH_NAMES.index(
            self.getOptionBox(
                title + "_DP_MonthOptionBox")) + 1
        year = int(self.getOptionBox(title + "_DP_YearOptionBox"))
        date = datetime.date(year, month, day)
        return date

    def getAllDatePickers(self):
        dps = {}
        for k in self.widgetManager.group(WIDGET_NAMES.DatePicker):
            dps[k] = self.getDatePicker(k)
        return dps

#####################################
# FUNCTIONS for ACCESSABILITY
#####################################

    def _makeAccess(self):
        if not self.accessMade:
            def _close(): self.hideSubWindow("access_access_subwindow")
            def _changeFg(): self.label("access_fg_colBox", bg=self.colourBox(self.getLabelBg("access_fg_colBox")))
            def _changeBg(): self.label("access_bg_colBox", bg=self.colourBox(self.getLabelBg("access_bg_colBox")))
            def _settings():
                font = {"underline":self.check("access_underline_check"), "overstrike":self.check("access_overstrike_check")}
                font["weight"] = "bold" if self.check("access_bold_check") is True else "normal"
                font["slant"] = "roman" if self.radio("access_italic_radio") == "Normal" else "italic"
                if len(self.listbox("access_family_listbox")) > 0: font["family"] = self.listbox("access_family_listbox")[0]
                if self.option("access_size_option") is not None: font["size"] = self.option("access_size_option")

                if self.check('access_label_check'): self.labelFont = font
                if self.check('access_input_check'): self.inputFont = font
                if self.check('access_button_check'): self.buttonFont = font

                self.bg = self.getLabelBg("access_bg_colBox")
                self.fg = self.getLabelBg("access_fg_colBox")

            self.accessOrigFont = self.accessOrigBg = self.accessOrigFg = None
            with self.subWindow("access_access_subwindow", sticky = "news", title="Accessibility", resizable=False) as sw:
                if not self.ttk:
                    sw.config(padx=5, pady=1)
                with self.labelFrame("access_font_labelframe", sticky="news", name="Font") as lf:
                    if not self.ttk:
                        lf.config(padx=5, pady=5, font=self._accessFont)

                    with self.frame("access_ticks_frame", colspan=2):
                        self.check("access_label_check", True, label="Labels", pos=(0,0), font=self._accessFont, tip="Set label fonts")
                        self.check("access_input_check", label="Inputs", pos=(0,1), font=self._accessFont, tip="Set input fonts")
                        self.check("access_button_check", label="Buttons", pos=(0,2), font=self._accessFont, tip="Set button fonts")

                    self.listbox("access_family_listbox", self.fonts, rows=6, tip="Choose a font", colspan=2, font=self._accessFont)
                    self.option("access_size_option", [7, 8, 9, 10, 12, 13, 14, 16, 18, 20, 22, 25, 29, 34, 40], label="Size:", tip="Choose a font size", font=self._accessFont)
                    self.check("access_bold_check", name="Bold", pos=('p',1), tip="Check this to make all font bold", font=self._accessFont)
                    self.radio("access_italic_radio", "Normal", tip="No italics", font=self._accessFont)
                    self.radio("access_italic_radio", "Italic", pos=('p',1), tip="Set font italic", font=self._accessFont)
                    self.check("access_underline_check", name="Underline", tip="Underline all text", font=self._accessFont)
                    self.check("access_overstrike_check", name="Overstrike", pos=('p',1), tip="Strike out all text", font=self._accessFont)
                with self.labelFrame("access_colour_labelframe", sticky="news", name="Colours") as lf:
                    if not self.ttk:
                        lf.config(padx=5, pady=5, font=self._accessFont)
                    self.label("access_fg_text", "Foreground:", sticky="ew", anchor="w", font=self._accessFont)
                    self.label("access_fg_colBox", "", pos=('p',1), sticky="ew", submit=_changeFg, relief="ridge", tip="Click here to set the foreground colour", font=self._accessFont, width=14)
                    self.label("access_bg_text", "Background:", sticky="ew", anchor="w", font=self._accessFont)
                    self.label("access_bg_colBox", "", pos=('p',1), sticky="ew", submit=_changeBg, relief="ridge", tip="Click here to set the background colour", font=self._accessFont, width=14)
                self.sticky="se"
                with self.frame("access_button_box"):
                    self.button("access_apply_button", _settings, name="Apply", pos=(0,0), font=self._accessFont)
                    self.button("access_reset_button", self._resetAccess, name="Reset", pos=(0,1), font=self._accessFont)
                    self.button("access_close_button", _close, name="Close", pos=(0,2), font=self._accessFont)
            self.accessMade = True

    def _resetAccess(self):
        if self.accessMade:
            self.check("access_label_check", True)
            self.check("access_input_check", False)
            self.check("access_button_check", False)

            self.listbox("access_family_listbox", self.accessOrigFont["family"])
            self.option("access_size_option", str(self.accessOrigFont["size"]))

            if self.accessOrigFont["weight"] == "normal": self.check("access_bold_check", False)
            else: self.check("access_bold_check", True)

            if self.accessOrigFont["slant"] == "roman": self.radio("access_italic_radio", "Normal")
            else: self.radio("access_italic_radio", "Italic")

            self.check("access_overstrike_check", self.accessOrigFont["overstrike"])
            self.check("access_underline_check", self.accessOrigFont["underline"])

            self.label("access_fg_colBox", bg=self.accessOrigFg)
            self.label("access_bg_colBox", bg=self.accessOrigBg)
        else:
            gui.warn("Accessibility not set up yet.")


    def showAccess(self, location=None):
        self._makeAccess()
        # update current settings
        self.accessOrigFont = self.font
        self.accessOrigBg = self.bg
        self.accessOrigFg = self.fg
        self._resetAccess()
        self.showSubWindow("access_access_subwindow")

#####################################
# FUNCTIONS for labels
#####################################

    def _parsePos(self, pos, kwargs):
        # alternative for specifying position
        if type(pos) != list and type(pos) != tuple: pos = (pos,)
        if len(pos) > 0: kwargs["row"] = pos[0]
        if len(pos) > 1: kwargs["column"] = pos[1]
        if len(pos) > 2: kwargs["colspan"] = pos[2]
        if len(pos) > 3: kwargs["rowspan"] = pos[3]

        # allow an alternative kwarg
        if "col" in kwargs: kwargs["column"]=kwargs.pop("col")

        # let user specify stickt/stretch/expan
        sticky = kwargs.pop("sticky", None)
        if sticky is not None: self.setSticky(sticky)
        stretch = kwargs.pop("stretch", None)
        if stretch is not None: self.setStretch(stretch)
        expand = kwargs.pop("expand", None)
        if expand is not None: self.setExpand(expand)

        return kwargs

    def label(self, title, value=None, *args, **kwargs):
        """ simpleGUI - adds, sets & gets labels all in one go """
        widgKind = WIDGET_NAMES.Label
        kind = kwargs.pop("kind", "standard").lower().strip()

        try: self.widgetManager.verify(widgKind, title)
        except: # widget exists
            if value is not None: self.setLabel(title, value)
            label = self.getLabel(title)
        else: # new widget
            kwargs = self._parsePos(kwargs.pop("pos", []), kwargs)
            if kind == "flash": label = self._labelMaker(title, value, kind, *args, **kwargs)
            elif kind == "selectable": label = self._labelMaker(title, value, kind, *args, **kwargs)
            else: label = self._labelMaker(title, value, "label", *args, **kwargs)

        if len(kwargs) > 0:
            self._configWidget(title, widgKind, **kwargs)
        return label

    def _labelMaker(self, title, text=None, kind="label", row=None, column=0, colspan=0, rowspan=0, **kwargs):
        """ Internal wrapper, to hide kwargs from original add functions """
        if kind == "flash": return self.addFlashLabel(title, text, row, column, colspan, rowspan)
        elif kind == "selectable": return self.addSelectableLabel(title, text, row, column, colspan, rowspan)
        elif kind == "label": return self.addLabel(title, text, row, column, colspan, rowspan)

    def _flash(self):
        if not self.alive: return
        if self.doFlash:
            for lab in self.widgetManager.group(WIDGET_NAMES.FlashLabel):
                bg = lab.cget("background")
                fg = lab.cget("foreground")
                lab.config(background=fg, foreground=bg)
        self.flashId = self.topLevel.after(250, self._flash)

    def addFlashLabel(self, title, text=None, row=None, column=0, colspan=0, rowspan=0):
        ''' adds a label with flashing text '''
        lab = self.addLabel(title, text, row, column, colspan, rowspan)
        self.widgetManager.log(WIDGET_NAMES.FlashLabel, lab)
        self.doFlash = True
        return lab

    def addSelectableLabel(self, title, text=None, row=None, column=0, colspan=0, rowspan=0):
        ''' adds a label with selectable text '''
        return self.addLabel(title, text, row, column, colspan, rowspan, selectable=True)

    def addLabel(self, title, text=None, row=None, column=0, colspan=0, rowspan=0, selectable=False):
        """Add a label to the GUI.
        :param title: a unique identifier for the Label
        :param text: optional text for the Label
        :param row/column/colspan/rowspan: the row/column to position the label in & how many rows/columns to strecth across
        :raises ItemLookupError: raised if the title is not unique
        """
        self.widgetManager.verify(WIDGET_NAMES.Label, title)
        if text is None:
            gui.trace("Not specifying text for labels (%s) now uses the title for the text. If you want an empty label, pass an empty string ''", title)
            text = title

        if not selectable:
            if not self.ttkFlag:
                lab = Label(self.getContainer(), text=text)
                lab.config(justify=LEFT, font=self._getContainerProperty('labelFont'), background=self._getContainerBg())
                lab.origBg = self._getContainerBg()
            else:
                lab = ttk.Label(self.getContainer(), text=text)
        else:
            lab = SelectableLabel(self.getContainer(), text=text)
            lab.config(justify=CENTER, font=self._getContainerProperty('labelFont'), background=self._getContainerBg())
            lab.origBg = self._getContainerBg()

        lab.inContainer = False
        lab.DEFAULT_TEXT = text

        self.widgetManager.add(WIDGET_NAMES.Label, title, lab)
        self._positionWidget(lab, row, column, colspan, rowspan)
        return lab

    def addEmptyLabel(self, title, row=None, column=0, colspan=0, rowspan=0):
        ''' adds an empty label '''
        return self.addLabel(title=title, text='', row=row, column=column, colspan=colspan, rowspan=rowspan)

    def addLabels(self, names, row=None, colspan=0, rowspan=0):
        ''' adds a set of labels, in the row, spannning specified columns '''
        frame = self._makeWidgetBox()(self.getContainer())
        if not self.ttkFlag:
            frame.config(background=self._getContainerBg())
        for i in range(len(names)):
            self.widgetManager.verify(WIDGET_NAMES.Label, names[i])
            if not self.ttkFlag:
                lab = Label(frame, text=names[i])
                lab.config(font=self._getContainerProperty('labelFont'), justify=LEFT, background=self._getContainerBg())
            else:
                lab = ttk.Label(frame, text=names[i])
            lab.DEFAULT_TEXT = names[i]
            lab.inContainer = False

            self.widgetManager.add(WIDGET_NAMES.Label, names[i], lab)
            lab.grid(row=0, column=i)
            Grid.columnconfigure(frame, i, weight=1)
            Grid.rowconfigure(frame, 0, weight=1)
            frame.theWidgets.append(lab)

        self._positionWidget(frame, row, 0, colspan, rowspan)
        self.widgetManager.log(WIDGET_NAMES.FrameBox, frame)

    def setLabel(self, name, text):
        lab = self.widgetManager.get(WIDGET_NAMES.Label, name)
        lab.config(text=text)

    def getLabel(self, name):
        lab = self.widgetManager.get(WIDGET_NAMES.Label, name)
        return lab.cget("text")

    def clearLabel(self, name):
        self.setLabel(name, "")

    def clearAllLabels(self):
        for lb in self.widgetManager.group(WIDGET_NAMES.Label):
            self.clearLabel(lb)

#####################################
# FUNCTIONS to add Text Area
#####################################

    def text(self, title, value=None, *args, **kwargs):
        """ simpleGUI - shortner for textArea() """
        return self.textArea(title, value, *args, **kwargs)

    def textArea(self, title, value=None, *args, **kwargs):
        """ adds, sets & gets textAreas all in one go """
        widgKind = WIDGET_NAMES.TextArea
        scroll = kwargs.pop("scroll", False)
        end = kwargs.pop("end", True)
        replace = kwargs.pop("replace", False)
        callFunction = kwargs.pop("callFunction", True)
        disabled = kwargs.pop("disabled", False)
        tag = kwargs.pop("tag", None)
        tags = kwargs.pop("tags", [])

        try: self.widgetManager.verify(WIDGET_NAMES.TextArea, title)
        except: # widget exists
            text = self.getTextArea(title)
        else: # new widget
            kwargs = self._parsePos(kwargs.pop("pos", []), kwargs)
            if scroll: text = self._textMaker(title, "scroll", *args, **kwargs)
            else: text = self._textMaker(title, "text", *args, **kwargs)
            callFunction = False

        # create any tags
        for _tag in tags:
            self.textAreaCreateTag(title, _tag[0], **_tag[1])

        if replace: self.clearTextArea(title)
        if value is not None: self.setTextArea(title, value, end=end, callFunction=callFunction, tag=tag)
        if disabled: self.disableTextArea(title)
        if len(kwargs) > 0:
            self._configWidget(title, widgKind, **kwargs)
        return text

    def _textMaker(self, title, kind="text", row=None, column=0, colspan=0, rowspan=0, *args, **kwargs):
        if kind == "scroll": return self.addScrolledTextArea(title, row, column, colspan, rowspan)
        elif kind == "text": return self.addTextArea(title, row, column, colspan, rowspan)

    def _buildTextArea(self, title, frame, scrollable=False):
        """ Internal wrapper, used for building TextAreas.

        :param title: the key used to reference this TextArea
        :param frame: this should be a container, used as the parent for the OptionBox
        :param scrollable: the key used to reference this TextArea
        :returns: the created TextArea
        :raises ItemLookupError: if the title is already in use
        """
        self.widgetManager.verify(WIDGET_NAMES.TextArea, title)
        if scrollable:
            text = AjScrolledText(frame)
        else:
            text = AjText(frame)
        text.config(width=20, height=10, undo=True, wrap=WORD)

        if not self.ttkFlag:
            if self.platform in [self.MAC, self.LINUX]:
                text.config(highlightbackground=self._getContainerBg())

        text.bind("<Tab>", self._focusNextWindow)
        text.bind("<Shift-Tab>", self._focusLastWindow)

        # add a right click menu
        text.var = None
        self._addRightClickMenu(text)

        self.widgetManager.add(WIDGET_NAMES.TextArea, title, text)
        self.logTextArea(title)

        return text

    def addTextArea(self, title, row=None, column=0, colspan=0, rowspan=0, text=None):
        """ Adds a TextArea with the specified title
        Simply calls internal _buildTextArea function before positioning the widget

        :param title: the key used to reference this TextArea
        :returns: the created TextArea
        :raises ItemLookupError: if the title is already in use
        """
        txt = self._buildTextArea(title, self.getContainer())
        self._positionWidget(txt, row, column, colspan, rowspan, N+E+S+W)
        if text is not None: self.setTextArea(title, text, callFunction=False)
        return txt

    def addScrolledTextArea(self, title, row=None, column=0, colspan=0, rowspan=0, text=None):
        """ Adds a Scrollable TextArea with the specified title
        Simply calls internal _buildTextArea functio, specifying a ScrollabelTextArea before positioning the widget

        :param title: the key used to reference this TextArea
        :returns: the created TextArea
        :raises ItemLookupError: if the title is already in use
        """
        txt = self._buildTextArea(title, self.getContainer(), True)
        self._positionWidget(txt, row, column, colspan, rowspan, N+E+S+W)
        if text is not None: self.setTextArea(title, text, callFunction=False)
        return txt

    def getTextArea(self, title):
        """ Gets the text in the specified TextArea

        :param title: the TextArea to check
        :returns: the text in the specified TextArea
        :raises ItemLookupError: if the title can't be found
        """
        return self.widgetManager.get(WIDGET_NAMES.TextArea, title).getText()

    def getAllTextAreas(self):
        """ Convenience function to get the text for all TextAreas in the GUI.

        :returns: a dictionary containing the result of calling getTextArea for every TextArea in the GUI
        """
        areas = {}
        for k in self.widgetManager.group(WIDGET_NAMES.TextArea):
            areas[k] = self.getTextArea(k)
        return areas

    def textAreaCreateTag(self, title, name, **kwargs):
        """ creates a new tag on the specified text area """
        ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title)
        ta.tag_config(name, **kwargs)

    def textAreaChangeTag(self, title, name, **kwargs):
        """ changes a tag on the specified text area """
        ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title)
        ta.tag_config(name, **kwargs)

    def textAreaDeleteTag(self, title, *tags):
        """ deletes the specified tag """
        ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title)
        ta.tag_delete(*tags)

    def textAreaTagPattern(self, title, tag, pattern, regexp=False):
        """ applies the tag to the specified text """
        ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title)
        ta.highlightPattern(pattern, tag, regexp=regexp)

    def textAreaTagRange(self, title, tag, start, end=END):
        """ applies the tag to the specified range """
        ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title)
        ta.tag_add(tag, start, end)

    def textAreaTagSelected(self, title, tag):
        if self.widgetManager.get(WIDGET_NAMES.TextArea, title).tag_ranges(SEL):
            self.textAreaTagRange(title, tag, SEL_FIRST, SEL_LAST)
        self.widgetManager.get(WIDGET_NAMES.TextArea, title).focus_set()

    def textAreaUntagRange(self, title, tag, start, end=END):
        """removes the tag from the specified range """
        ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title)
        ta.tag_remove(tag, start, end)

    def textAreaToggleFontRange(self, title, tag, start, end=END):
        """ will toggle the tag at the specified range """
        ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title)
        tag = ta.verifyFontTag(tag)

        if tag in ta.tag_names(start):
            ta.tag_remove("AJ_"+tag, start, end)
        else:
            self.textAreaApplyFontRange(title, tag, start, end)

    def textAreaToggleFontSelected(self, title, tag):
        if self.widgetManager.get(WIDGET_NAMES.TextArea, title).tag_ranges(SEL):
            self.textAreaToggleFontRange(title, tag, SEL_FIRST, SEL_LAST)
        self.widgetManager.get(WIDGET_NAMES.TextArea, title).focus_set()

    def textAreaApplyFontSelected(self, title, tag):
        if self.widgetManager.get(WIDGET_NAMES.TextArea, title).tag_ranges(SEL):
            self.textAreaApplyFontRange(title, tag, SEL_FIRST, SEL_LAST)
        self.widgetManager.get(WIDGET_NAMES.TextArea, title).focus_set()

    def textAreaApplyFontRange(self, title, tag, start, end=END):
        """removes the tag from the specified range """
        ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title)
        tag = ta.verifyFontTag(tag)
        if tag != "UNDERLINE":
            ta.tag_remove("AJ_BOLD", start, end)
            ta.tag_remove("AJ_ITALIC", start, end)
            ta.tag_remove("AJ_BOLD_ITALIC", start, end)
        ta.tag_add("AJ_" + tag, start, end)

    def textAreaUntagSelected(self, title, tag):
        if self.widgetManager.get(WIDGET_NAMES.TextArea, title).tag_ranges(SEL):
            self.textAreaUntagRange(title, tag, SEL_FIRST, SEL_LAST)
        self.widgetManager.get(WIDGET_NAMES.TextArea, title).focus_set()

    def textAreaToggleTagRange(self, title, tag, start, end=END):
        """ will toggle the tag at the specified range """
        ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title)
        if tag in ta.tag_names(start): self.textAreaUntagRange(title, tag, start, end)
        else: self.textAreaTagRange(title, tag, start, end)

    def textAreaToggleTagSelected(self, title, tag):
        if self.widgetManager.get(WIDGET_NAMES.TextArea, title).tag_ranges(SEL):
            self.textAreaToggleTagRange(title, tag, SEL_FIRST, SEL_LAST)
        self.widgetManager.get(WIDGET_NAMES.TextArea, title).focus_set()

    def searchTextArea(self, title, pattern, start=None, stop=None, nocase=True, backwards=False):
        """ will find and highlight the specified text, returning the position """
        ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title)
        if start is None: start = ta.index(INSERT)
        pos = ta.search(pattern, start, stopindex=stop, nocase=nocase, backwards=backwards)
        ta.focus_set()
        if pos == "":
            return None
        else:
            end = str(pos) + " + " + str(len(pattern)) + " c"
            ta.see(pos)
            ta.tag_add(SEL, pos, end)
            ta.mark_set("insert", pos)
            return pos

    def getTextAreaTag(self, title, tag):
        """ returns all details about the specified tag """
        ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title)
        return ta.tag_config(tag)

    def getTextAreaTags(self, title):
        """ returns a list of all tags in the text area """
        ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title)
        return ta.tag_names()

    def setTextAreaFont(self, title, **kwargs):
        """ changes the font of a text area """
        self.widgetManager.get(WIDGET_NAMES.TextArea, title).setFont(**kwargs)

    def setTextArea(self, title, text, end=True, callFunction=True, tag=None):
        """ Add the supplied text to the specified TextArea

        :param title: the TextArea to change
        :param text: the text to add to the TextArea
        :param end: where to insert the text, by default it is added to the end. Set end to False to add to the beginning.
        :param callFunction: whether to generate an event to notify that the widget has changed
        :returns: None
        :raises ItemLookupError: if the title can't be found
        """
        ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title)

        ta.pauseCallFunction(callFunction)
        # in case it's disabled
        _state = ta.cget('state')
        ta.config(state='normal')

        if end:
            pos = ta.index('end -1c linestart')
            ta.insert(END, text)
            ta.see(END)
#            if tag is not None: self.textAreaTagRange(title, tag, pos)
        else:
            ta.insert('1.0', text)
            ta.see('1.0')
#            if tag is not None: ta.textAreaTagPattern(title, tag, text)

        ta.config(state=_state)
        ta.resumeCallFunction()

    def clearTextArea(self, title, callFunction=True):
        """ Removes all text from the specified TextArea

        :param title: the TextArea to change
        :param callFunction: whether to generate an event to notify that the widget has changed
        :returns: None
        :raises ItemLookupError: if the title can't be found
        """
        ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title)
        ta.pauseCallFunction(callFunction)
        # in case it's disabled
        _state = ta.cget('state')
        ta.config(state='normal')
        ta.delete('1.0', END)
        ta.config(state=_state)
        ta.resumeCallFunction()

    def clearAllTextAreas(self, callFunction=False):
        """ Convenience function to clear all TextAreas in the GUI
        Will simply call clearTextArea on each TextArea

        :param callFunction: whether to generate an event to notify that the widget has changed
        :returns: None
        """
        for ta in self.widgetManager.group(WIDGET_NAMES.TextArea):
            self.clearTextArea(ta, callFunction=callFunction)

    def highlightTextArea(self, title, start, end=END):
        """ selects text in the specified range """
        ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title)
        ta.tag_add(SEL, start, end)

    def logTextArea(self, title):
        """ Creates an md5 hash - can be used later to check if the TextArea has changed
        The hash is stored in the widget

        :param title: the TextArea to hash
        :returns: None
        :raises ItemLookupError: if the title can't be found
        """
        self._loadHashlib()
        if hashlib is False:
            self.warn("Unable to log TextArea, hashlib library not available")
        else:
            text = self.widgetManager.get(WIDGET_NAMES.TextArea, title)
            text.__hash = text.getTextAreaHash()

    def textAreaChanged(self, title):
        """ Creates a temporary md5 hash - and compares it with a previously generated & stored hash
        The previous hash has to be generated manually, by calling logTextArea

        :param title: the TextArea to hash
        :returns: bool - True if the TextArea has changed or False if it hasn't
        :raises ItemLookupError: if the title can't be found
        """
        self._loadHashlib()
        if hashlib is False:
            self.warn("Unable to log TextArea, hashlib library not available")
        else:
            text = self.widgetManager.get(WIDGET_NAMES.TextArea, title)
            return text.__hash != text.getTextAreaHash()

#####################################
# FUNCTIONS to add Tree Widgets
#####################################

    def tree(self, title, value=None, *args, **kwargs):
        """ simpleGUI - adds, sets & gets trees all in one go """
        widgKind = WIDGET_NAMES.Tree

        click = kwargs.pop("click", None)
        dblClick = kwargs.pop("dbl", None)
        edit = kwargs.pop("edit", None)
        editable = kwargs.pop("editable", None)
        showAttr = kwargs.pop("attributes", None)
        showMenu = kwargs.pop("menu", None)

        fg = kwargs.pop("fg", None)
        bg = kwargs.pop("bg", None)
        fgH = kwargs.pop("fgH", None)
        bgH = kwargs.pop("bgH", None)

        try: self.widgetManager.verify(widgKind, title)
        except: # widget exists
            tree = self.getTree(title)
        else: # new widget
            kwargs = self._parsePos(kwargs.pop("pos", []), kwargs)
            tree = self.addTree(title, value, *args, **kwargs)

        if len(kwargs) > 0:
            self._configWidget(title, widgKind, **kwargs)

        self.setTreeColours(title, fg, bg, fgH, bgH)

        if click is not None: self.setTreeClickFunction(title, click)
        if edit is not None: self.setTreeEditFunction(title, edit)
        if dblClick is not None: self.setTreeDoubleClickFunction(title, dblClick)
        if editable is not None: self.setTreeEditable(title, editable)
        if showAttr is not None: self.showTreeAttributes(title, showAttr)
        if showMenu is not None: self.showTreeMenu(title, showMenu)
        return tree

    def addTree(self, title, data, row=None, column=0, colspan=0, rowspan=0):
        ''' adds a navigatable tree, displaying the specified xml text '''
        self.widgetManager.verify(WIDGET_NAMES.Tree, title)
        self._importAjtree()
        if parseString is False:
            self.warn("Unable to parse xml files. .addTree() not available")
            return

        if isinstance(data, UNIVERSAL_STRING):
            data = parseString(data)
        else:
            pass # assume xml object
            
        return self._buildTree(title, data, row, column, colspan, rowspan)

    def _buildTree(self, title, xmlDoc, row=None, column=0, colspan=0, rowspan=0):
        self.widgetManager.verify(WIDGET_NAMES.Tree, title)

        frame = ScrollPane(
            self.getContainer(),
            relief=RAISED,
            borderwidth=2,
            bg="#FFFFFF",
            highlightthickness=0,
            takefocus=1)
        self._positionWidget(frame, row, column, colspan, rowspan, "NSEW")

        treeData = self._makeAjTreeData()(xmlDoc)
        gui.trace("TreeData populated: %s", title)

        treeNode = self._makeAjTreeNode()(frame.getPane(), None, treeData)
        gui.trace("TreeNode created: %s", title)

        self.widgetManager.add(WIDGET_NAMES.Tree, title, treeNode)
        # update() & expand() called in go() function
        return treeNode

    # not complete yet...
    def clearTree(self, title):
        tree = self.widgetManager.get(WIDGET_NAMES.Tree, title)
        tree.destroy()
        tree.update()

    def showTreeAttributes(self, title, show=True):
        tree = self.widgetManager.get(WIDGET_NAMES.Tree, title)
        self._loadTooltip()
        tree.showAttributes(show)

    # not complete yet...
    def showTreeMenu(self, title, show=True):
        tree = self.widgetManager.get(WIDGET_NAMES.Tree, title)
        tree.showMenu(show)

    # not complete yet...
    def addTreeChild(self, title, data):
        tree = self.widgetManager.get(WIDGET_NAMES.Tree, title)
        if isinstance(data, UNIVERSAL_STRING):
            data = parseString(data)
        treeData = self._makeAjTreeData()(data)
        tree.addChild(treeData)

    def setTreeEditable(self, title, value=True):
        tree = self.widgetManager.get(WIDGET_NAMES.Tree, title)
        tree.item.setCanEdit(value)

    def setTreeBg(self, title, colour):
        tree = self.widgetManager.get(WIDGET_NAMES.Tree, title)
        tree.setBgColour(colour)

    def setTreeFg(self, title, colour):
        tree = self.widgetManager.get(WIDGET_NAMES.Tree, title)
        tree.setFgColour(colour)

    def setTreeHighlightBg(self, title, colour):
        tree = self.widgetManager.get(WIDGET_NAMES.Tree, title)
        tree.setBgHColour(colour)

    def setTreeHighlightFg(self, title, colour):
        tree = self.widgetManager.get(WIDGET_NAMES.Tree, title)
        tree.setFgHColour(colour)

    def setTreeColours(self, title, fg=None, bg=None, fgH=None, bgH=None):
        tree = self.widgetManager.get(WIDGET_NAMES.Tree, title)
        tree.setAllColours(bg, fg, bgH, fgH)

    def setTreeDoubleClickFunction(self, title, func):
        if func is not None:
            tree = self.widgetManager.get(WIDGET_NAMES.Tree, title)
            tree.item.registerDblClick(title, func)

    def setTreeClickFunction(self, title, func):
        if func is not None:
            tree = self.widgetManager.get(WIDGET_NAMES.Tree, title)
            tree.item.registerClick(title, func)

    def setTreeEditFunction(self, title, func):
        if func is not None:
            tree = self.widgetManager.get(WIDGET_NAMES.Tree, title)
            command = self.MAKE_FUNC(func, title)
            tree.registerEditEvent(command)

    # get whole tree as XML
    def getTreeXML(self, title):
        tree = self.widgetManager.get(WIDGET_NAMES.Tree, title)
        return tree.item.node.toxml()

    # get selected node as a string
    def getTreeSelected(self, title):
        tree = self.widgetManager.get(WIDGET_NAMES.Tree, title)
        return tree.getSelectedText()

    # get selected node (and children) as XML
    def getTreeSelectedXML(self, title):
        tree = self.widgetManager.get(WIDGET_NAMES.Tree, title)
        item = tree.getSelected()
        if item is not None:
            return item.node.toxml()
        else:
            return None

    def generateTree(self, title):
        """ displays data inside tree """
        tree = self.widgetManager.get(WIDGET_NAMES.Tree, title)
        gui.trace("Generating Tree: %s", title)
        tree.update()
        gui.trace("Tree updated: %s", title)
        tree.expand()
        gui.trace("Tree expanded: %s", title)

#####################################
# FUNCTIONS to add Message Box
#####################################

    def message(self, title, value=None, *args, **kwargs):
        """ simpleGUI - adds, sets & gets messages all in one go """
        widgKind = WIDGET_NAMES.Message

        try: self.widgetManager.verify(WIDGET_NAMES.Message, title)
        except: # widget exists
            if value is not None: self.setMessage(title, value)
            msg = self.getMessage(title)
        else: # new widget
            kwargs = self._parsePos(kwargs.pop("pos", []), kwargs)
            msg = self._messageMaker(title, value, *args, **kwargs)

        if len(kwargs) > 0:
            self._configWidget(title, widgKind, **kwargs)

        return msg

    def _messageMaker(self, title, text, row=None, column=0, colspan=0, rowspan=0, *args, **kwargs):
        return self.addMessage(title, text, row, column, colspan, rowspan)

    def addMessage(self, title, text=None, row=None, column=0, colspan=0, rowspan=0):
        ''' adds a message box, to display text across multiple lines '''
        self.widgetManager.verify(WIDGET_NAMES.Message, title)

        if text is None:
            text = title
            gui.trace("Not specifying text for messages (%s) now uses the title for the text. If you want an empty message, pass an empty string ''", title)
        mess = Message(self.getContainer())
        mess.config(text=text)
        mess.config(font=self._getContainerProperty('labelFont'))
        mess.config(justify=LEFT, background=self._getContainerBg())
        mess.DEFAULT_TEXT = text

        if self.platform in [self.MAC, self.LINUX]:
            mess.config(highlightbackground=self._getContainerBg())

        self.widgetManager.add(WIDGET_NAMES.Message, title, mess)

        self._positionWidget(mess, row, column, colspan, rowspan)
#            mess.bind("<Configure>", lambda e: mess.config(width=e.width-10))
        return mess

    def addEmptyMessage(self, title, row=None, column=0, colspan=0, rowspan=0):
        ''' adds an empty message box '''
        return self.addMessage(title, "", row, column, colspan, rowspan)

    def setMessage(self, title, text):
        mess = self.widgetManager.get(WIDGET_NAMES.Message, title)
        mess.config(text=text)

    def setMessageAspect(self, title, aspect):
        """ set a new aspect ratio for the text in this widget """
        mess = self.widgetManager.get(WIDGET_NAMES.Message, title)
        mess.config(aspect=aspect)

    def clearMessage(self, title):
        self.setMessage(title, "")

    def getMessage(self, title):
        mess = self.widgetManager.get(WIDGET_NAMES.Message, title)
        return mess.cget("text")

#####################################
# FUNCTIONS for entry boxes
#####################################

    def entry(self, title, value=None, *args, **kwargs):
        """ simpleGUI - adds, sets & gets entries all in one go """
        widgKind = WIDGET_NAMES.Entry
        default = kwargs.pop("default", None)
        limit = kwargs.pop("limit", None)
        case = kwargs.pop("case", None)
        rows = kwargs.pop("rows", None)
        secret = kwargs.pop("secret", False)
        kind = kwargs.pop("kind", "standard").lower().strip()
        labBg = kwargs.pop("labBg", None)

        try: self.widgetManager.verify(WIDGET_NAMES.Entry, title)
        except: # widget exists
            if value is not None: self.setEntry(title, value, *args, **kwargs)
            ent = self.getEntry(title)
        else: # new widget
            kwargs = self._parsePos(kwargs.pop("pos", []), kwargs)
            # create the entry widget
            if kind == "auto":
                if value is None: value = []
                ent = self._entryMaker(title, *args, secret=secret, kind=kind, words=value, **kwargs)
            else:
                ent = self._entryMaker(title, *args, secret=secret, kind=kind, **kwargs)
                if not ent: return

        # apply any setter values
        if limit is not None: self.setEntryMaxLength(title, limit)
        if case == "upper": self.setEntryUpperCase(title)
        elif case == "lower": self.setEntryLowerCase(title)

        if default is not None: self.setEntryDefault(title, default)

        if kind != "auto":
            if value is not None: self.setEntry(title, value)
        else:
            if rows is not None: self.setAutoEntryNumRows(title, rows)

        if labBg is not None and self.widgetManager.get(WIDGET_NAMES.Entry, title).isValidation:
            self.setValidationEntryLabelBg(title, labBg)

        # used by file entries
        kwargs.pop("text", None)

        if len(kwargs) > 0:
            self._configWidget(title, widgKind, **kwargs)
        return ent

    def setValidationEntryLabelBg(self, title, bg):
        ent = self.widgetManager.get(WIDGET_NAMES.Entry, title)
        if not ent.isValidation:
            raise Exception("You can only set label BGs on validation entries")
        ent.lab.config(bg=bg)

    def _entryMaker(self, title, row=None, column=0, colspan=0, rowspan=0, secret=False, label=False, kind="standard", words=None, **kwargs):
        # used by file entries
        text = kwargs.pop("text", None) 
        default = kwargs.pop("default", None) 

        if not label:
            frame = self.getContainer()
        else:
            frame = self._getLabelBox(title, label=label, **kwargs)

        if kind == "standard":
            ent = self._buildEntry(title, frame, secret)
        elif kind == "numeric":
            ent = self._buildEntry(title, frame, secret)
            if self.validateNumeric is None:
                self.validateNumeric = (self.containerStack[0]['container'].register(
                    self._validateNumericEntry), '%d', '%i', '%P', '%s', '%S', '%v', '%V', '%W')

            ent.isNumeric = True
            ent.config(validate='key', validatecommand=self.validateNumeric)
            self.setEntryTooltip(title, "Numeric data only.")
        elif kind == "auto":
            ent = self._buildEntry(title, frame, secret=False, words=words)
        elif kind in ["file", "open", "save", "directory"]:
            ent = self._buildFileEntry(title, frame, kind=kind, text=text, default=default)
        elif kind == "validation":
            ent = self._buildValidationEntry(title, frame, secret)
        else:
            raise Exception("Invalid entry kind: %s", kind)

        if not label:
            self._positionWidget(ent, row, column, colspan, rowspan)
        else:
            self._packLabelBox(frame, ent)
            self._positionWidget(frame, row, column, colspan, rowspan)
        return ent

    def addEntry(self, title, row=None, column=0, colspan=0, rowspan=0, secret=False):
        ''' adds an entry box for capturing text '''
        return self._entryMaker(title, row, column, colspan, rowspan, secret=secret, label=False, kind="standard")

    def addLabelEntry(self, title, row=None, column=0, colspan=0, rowspan=0, secret=False, label=True):
        ''' adds an entry box for capturing text, with the title as a label '''
        return self._entryMaker(title, row, column, colspan, rowspan, secret, label=label)

    def addSecretEntry(self, title, row=None, column=0, colspan=0, rowspan=0):
        ''' adds an entry box for capturing text, where the text is displayed as stars '''
        return self._entryMaker(title, row, column, colspan, rowspan, True)

    def addLabelSecretEntry(self, title, row=None, column=0, colspan=0, rowspan=0, label=True):
        ''' adds an entry box for capturing text, where the text is displayed as stars, with the title as a label '''
        return self._entryMaker(title, row, column, colspan, rowspan, secret=True, label=label)

    def addSecretLabelEntry(self, title, row=None, column=0, colspan=0, rowspan=0, label=True):
        ''' adds an entry box for capturing text, where the text is displayed as stars, with the title as a label '''
        return self._entryMaker(title, row, column, colspan, rowspan, secret=True, label=label)

    def addFileEntry(self, title, row=None, column=0, colspan=0, rowspan=0):
        ''' adds an entry box with a button, that pops-up a file dialog '''
        return self._entryMaker(title, row, column, colspan, rowspan, secret=False, label=False, kind="file")

    def addLabelFileEntry(self, title, row=None, column=0, colspan=0, rowspan=0, label=True):
        ''' adds an entry box with a button, that pops-up a file dialog, with a label that displays the title '''
        return self._entryMaker(title, row, column, colspan, rowspan, secret=False, label=label, kind="file")

    def addOpenEntry(self, title, row=None, column=0, colspan=0, rowspan=0):
        ''' adds an entry box with a button, that pops-up a open dialog '''
        return self._entryMaker(title, row, column, colspan, rowspan, secret=False, label=False, kind="open")

    def addLabelOpenEntry(self, title, row=None, column=0, colspan=0, rowspan=0, label=True):
        ''' adds an entry box with a button, that pops-up a open dialog, with a label that displays the title '''
        return self._entryMaker(title, row, column, colspan, rowspan, secret=False, label=label, kind="open")

    def addSaveEntry(self, title, row=None, column=0, colspan=0, rowspan=0):
        ''' adds an entry box with a button, that pops-up a save dialog '''
        return self._entryMaker(title, row, column, colspan, rowspan, secret=False, label=False, kind="save")

    def addLabelSaveEntry(self, title, row=None, column=0, colspan=0, rowspan=0, label=True):
        ''' adds an entry box with a button, that pops-up a save dialog, with a label that displays the title '''
        return self._entryMaker(title, row, column, colspan, rowspan, secret=False, label=label, kind="save")

    def addDirectoryEntry(self, title, row=None, column=0, colspan=0, rowspan=0):
        return self._entryMaker(title, row, column, colspan, rowspan, secret=False, label=False, kind="directory")

    def addLabelDirectoryEntry(self, title, row=None, column=0, colspan=0, rowspan=0, label=True):
        return self._entryMaker(title, row, column, colspan, rowspan, secret=False, label=label, kind="directory")

    def addValidationEntry(self, title, row=None, column=0, colspan=0, rowspan=0, secret=False):
        return self._entryMaker(title, row, column, colspan, rowspan, secret=False, label=False, kind="validation")

    def addLabelValidationEntry(self, title, row=None, column=0, colspan=0, rowspan=0, secret=False, label=True):
        return self._entryMaker(title, row, column, colspan, rowspan, secret=False, label=label, kind="validation")

    def addAutoEntry(self, title, words, row=None, column=0, colspan=0, rowspan=0):
        return self._entryMaker(title, row, column, colspan, rowspan, secret=False, label=False, kind="auto", words=words)

    def addLabelAutoEntry(self, title, words, row=None, column=0, colspan=0, rowspan=0, secret=False, label=True):
        return self._entryMaker(title, row, column, colspan, rowspan, secret=False, label=label, kind="auto", words=words)

    def addNumericEntry(self, title, row=None, column=0, colspan=0, rowspan=0, secret=False):
        return self._entryMaker(title, row, column, colspan, rowspan, secret=secret, label=False, kind="numeric")

    def addLabelNumericEntry(self, title, row=None, column=0, colspan=0, rowspan=0, secret=False, label=True):
        return self._entryMaker(title, row, column, colspan, rowspan, secret=secret, label=label, kind="numeric")

    def addNumericLabelEntry(self, title, row=None, column=0, colspan=0, rowspan=0, secret=False, label=True):
        return self._entryMaker(title, row, column, colspan, rowspan, secret=secret, label=label, kind="numeric")

    def _getDirName(self, title):
        self._getFileName(title, kind='directory')

    def _getSaveName(self, title):
        self._getFileName(title, kind='save')

    def _getFileName(self, title, kind='open'):
        if kind in ['open', 'file']:
            fileName = self.openBox()
        elif kind == 'save':
            fileName = self.saveBox()
        elif kind == 'directory':
            fileName = self.directoryBox()

        if fileName is not None and fileName != "":
            self.setEntry(title, fileName)

        self.topLevel.after(250, self.setEntryFocus, title)

    def _checkDirName(self, title):
        if len(self.getEntry(title)) == 0:
            self._getFileName(title, kind='directory')

    def _checkSaveName(self, title):
        if len(self.getEntry(title)) == 0:
            self._getFileName(title, kind='save')

    def _checkFileName(self, title):
        if len(self.getEntry(title)) == 0:
            self._getFileName(title, kind='open')

    def _buildEntry(self, title, frame, secret=False, words=[]):
        self.widgetManager.verify(WIDGET_NAMES.Entry, title)
        # if we are an autocompleter
        if len(words) > 0:
            ent = self._makeAutoCompleteEntry()(words, self._getTopLevel(), frame)
        else:
            var = StringVar(self.topLevel)
            ent = entryBase(frame, textvariable=var)
            ent.var = var
            ent.var.auto_id = None

            # for now - suppress UP/DOWN arrows
            if self.platform in [self.MAC]:
                def suppress(event):
                    if event.keysym == "Up":
                        # move home
                        event.widget.icursor(0)
                        event.widget.xview(0)
                        return "break"
                    elif event.keysym == "Down":
                        # move end
                        if not self.ttkFlag:
                            event.widget.icursor(END)
                            event.widget.xview(END)
                        else:
                            event.widget.icursor(END)
                            event.widget.xview(len(event.widget.get()))
                        return "break"

                ent.bind("<Key>", suppress)

        if not self.ttkFlag:
            ent.config(font=self._getContainerProperty('inputFont'))
            if self.platform in [self.MAC, self.LINUX]:
                ent.config(highlightbackground=self._getContainerBg())

        # vars to store any limit traces
        ent.var.uc_id = None
        ent.var.lc_id = None
        ent.var.ml_id = None

        ent.inContainer = False
        ent.showingDefault = False  # current status of entry
        ent.default = ""  # the default value to show (if set)
        ent.DEFAULT_TEXT = ""  # the default value for language support
        ent.myTitle = title  # the title of the entry
        ent.isNumeric = False  # if the entry is numeric
        ent.isValidation = False  # if the entry is validation
        ent.isSecret = False  # if the entry is secret

        # configure it to be secret
        if secret:
            ent.config(show="*")
            ent.isSecret = True

        ent.bind("<Tab>", self._focusNextWindow)
        ent.bind("<Shift-Tab>", self._focusLastWindow)

        # add a right click menu
        self._addRightClickMenu(ent)

        self.widgetManager.add(WIDGET_NAMES.Entry, title, ent)
        self.widgetManager.add(WIDGET_NAMES.Entry, title, ent.var, group=WidgetManager.VARS)
        return ent

    def _buildFileEntry(self, title, frame, kind='save', text=None, default=None):

        vFrame = self._makeButtonBox()(frame)
        self.widgetManager.log(WIDGET_NAMES.FrameBox, vFrame)

        if not self.ttkFlag:
            vFrame.config(background=self._getContainerBg())

        vFrame.theWidget = self._buildEntry(title, vFrame)
        vFrame.theWidget.inContainer = True
        vFrame.theWidget.pack(expand=True, fill=X, side=LEFT)

        if kind in ['open', "file"]:
            command = self.MAKE_FUNC(self._getFileName, title)
            vFrame.theWidget.click_command = self.MAKE_FUNC(self._checkFileName, title)
            if text is None: text = "File"
            if default is None: default = "-- enter a filename --"
        elif kind == 'save':
            command = self.MAKE_FUNC(self._getSaveName, title)
            vFrame.theWidget.click_command = self.MAKE_FUNC(self._checkSaveName, title)
            if text is None: text = "File"
            if default is None: default = "-- enter a filename --"
        else:
            command = self.MAKE_FUNC(self._getDirName, title)
            vFrame.theWidget.click_command = self.MAKE_FUNC(self._checkDirName, title)
            if text is None: text = "Directory"
            if default is None: default = "-- enter a directory --"

        self.setEntryDefault(title, default)
        vFrame.theWidget.bind("<Button-1>", vFrame.theWidget.click_command, "+")

        if not self.ttkFlag:
            vFrame.theButton = Button(vFrame, font=self._getContainerProperty('buttonFont'))
        else:
            vFrame.theButton = ttk.Button(vFrame)

        vFrame.theButton.config(text=text)
        vFrame.theButton.config(command=command)
        vFrame.theButton.pack(side=RIGHT, fill=X)
        vFrame.theButton.inContainer = True
        vFrame.theButton.SKIP_CLEANSE = True
        vFrame.theWidget.but = vFrame.theButton

        if not self.ttkFlag and self.platform in [self.MAC, self.LINUX]:
            vFrame.theButton.config(highlightbackground=self._getContainerBg())

        return vFrame

    def _buildValidationEntry(self, title, frame, secret):
        vFrame = self._makeLabelBox()(frame)
        self.widgetManager.log(WIDGET_NAMES.FrameBox, vFrame)
        vFrame.isValidation = True

        ent = self._buildEntry(title, vFrame, secret)
        if not self.ttkFlag:
            vFrame.config(background=self._getContainerBg())
            ent.config(highlightthickness=2)
        ent.pack(expand=True, fill=X, side=LEFT)
        ent.isValidation = True
        ent.inContainer = True

        class ValidationLabel(labelBase, object):
            def __init__(self, parent, *args, **options):
                super(ValidationLabel, self).__init__(parent, *args, **options)

        lab = ValidationLabel(vFrame)
        lab.pack(side=RIGHT, fill=Y)
        lab.config(font=self._getContainerProperty('labelFont'))
        if not self.ttkFlag:
            lab.config(background=self._getContainerBg())
        lab.inContainer = True
        lab.isValidation = True
        ent.lab = lab

        vFrame.theWidget = ent
        vFrame.theLabel = lab
        self.setEntryWaitingValidation(title)

        return vFrame

    def setEntryValid(self, title):
        self.setValidationEntry(title, "valid")

    def setEntryInvalid(self, title):
        self.setValidationEntry(title, "invalid")

    def setEntryWaitingValidation(self, title):
        self.setValidationEntry(title, "wait")

    def setValidationEntry(self, title, state="valid"):

        entry = self.widgetManager.get(WIDGET_NAMES.Entry, title)
        if not entry.isValidation:
            self.warn("Entry %s is not a validation entry. Unable to set WAITING VALID.", title)
            return

        if state == "wait":
            col = "#000000"
            text = '\u2731'
            eStyle="ValidationEntryWaiting.TEntry"
            lStyle="ValidationEntryWaiting.TLabel"
        elif state == "invalid":
            col = "#FF0000"
            text = '\u2716'
            eStyle="ValidationEntryInvalid.TEntry"
            lStyle="ValidationEntryInvalid.TLabel"
        elif state == "valid":
            col = "#4CC417"
            text = '\u2714'
            eStyle="ValidationEntryValid.TEntry"
            lStyle="ValidationEntryValid.TLabel"
        else:
            self.warn("Invalid validation state: %s", state)
            return

        if not self.ttkFlag:
            if not entry.showingDefault:
                entry.config(fg=col)
            entry.config(highlightbackground=col, highlightcolor=col)
            entry.config(highlightthickness=1)
            entry.lab.config(text=text, fg=col)
            entry.oldFg = col
        else:
            if not entry.showingDefault:
                entry.configure(style=eStyle)
            entry.lab.config(text=text, style=lStyle)
            entry.oldFg = eStyle

        entry.lab.DEFAULT_TEXT = entry.lab.cget("text")

    def appendAutoEntry(self, title, value):
        entry = self.widgetManager.get(WIDGET_NAMES.Entry, title)
        try:
            entry.addWords(value)
        except AttributeError:
            gui.error("You can only append items to an AutoEntry, %s is not an AutoEntry.", title)

    def removeAutoEntry(self, title, value):
        entry = self.widgetManager.get(WIDGET_NAMES.Entry, title)
        try:
            entry.removeWord(value)
        except AttributeError:
            gui.error("You can only remove items from an AutoEntry, %s is not an AutoEntry.", title)

    def changeAutoEntry(self, title, value):
        entry = self.widgetManager.get(WIDGET_NAMES.Entry, title)
        try:
            entry.changeWords(value)
        except AttributeError:
            gui.error("You can only change items in an AutoEntry, %s is not an AutoEntry.", title)

    def setAutoEntryNumRows(self, title, rows):
        entry = self.widgetManager.get(WIDGET_NAMES.Entry, title)
        try:
            entry.setNumRows(rows)
        except AttributeError:
            gui.error("You can only change the number of rows in an AutoEntry, %s is not an AutoEntry.", title)

    def _validateNumericEntry(self, action, index, value_if_allowed, prior_value, text, validation_type, trigger_type, widget_name):
        if action == "1":
            if str(text) in '0123456789.-+':
                try:
                    if len(str(value_if_allowed)) == 1 and str(value_if_allowed) in '.-':
                        return True
                    elif len(str(value_if_allowed)) == 2 and str(value_if_allowed) == '-.':
                        return True
                    else:
                        float(value_if_allowed)
                        return True
                except ValueError:
                    self.containerStack[0]['container'].bell()
                    return False
            else:
                self.containerStack[0]['container'].bell()
                return False
        else:
            return True

    def getEntry(self, name):
        entry = self.widgetManager.get(WIDGET_NAMES.Entry, name)
        if entry.showingDefault:
            if entry.isNumeric:
                return None
            else:
                return ""
        else:
            val = self.widgetManager.get(WIDGET_NAMES.Entry, name, group=WidgetManager.VARS).get()
            if entry.isNumeric:
                if len(val) == 0 or (len(val) == 1 and val in '.-') or (len(val) == 2 and val == "-."):
                    return None
                else:
                    return float(val)
            else:
                return val

    def getAllEntries(self):
        entries = {}
        for k in self.widgetManager.group(WIDGET_NAMES.Entry):
            entries[k] = self.getEntry(k)
        return entries

    def setEntry(self, name, text, callFunction=True):
        ent = self.widgetManager.get(WIDGET_NAMES.Entry, name)
        var = self.widgetManager.get(WIDGET_NAMES.Entry, name, group=WidgetManager.VARS)
        self._updateEntryDefault(name, mode="set")

        # now call function
        with PauseCallFunction(callFunction, var, False):
            if not ent.isNumeric or self._validateNumericEntry("1", None, text, None, "1", None, None, None):
                var.set(text)

    def setEntryMaxLength(self, name, length):
        var = self.widgetManager.get(WIDGET_NAMES.Entry, name, group=WidgetManager.VARS)
        var.maxLength = length
        if var.ml_id is not None:
            var.trace_vdelete('w', var.ml_id)
        var.ml_id = var.trace('w', self.MAKE_FUNC(self._limitEntry, name))

    def setEntryUpperCase(self, name):
        var = self.widgetManager.get(WIDGET_NAMES.Entry, name, group=WidgetManager.VARS)
        if var.uc_id is not None:
            var.trace_vdelete('w', var.uc_id)
        var.uc_id = var.trace('w', self.MAKE_FUNC(self._upperEntry, name))

    def setEntryLowerCase(self, name):
        var = self.widgetManager.get(WIDGET_NAMES.Entry, name, group=WidgetManager.VARS)
        if var.lc_id is not None:
            var.trace_vdelete('w', var.lc_id)
        var.lc_id = var.trace('w', self.MAKE_FUNC(self._lowerEntry, name))

    def _limitEntry(self, name):
        var = self.widgetManager.get(WIDGET_NAMES.Entry, name, group=WidgetManager.VARS)
        if len(var.get()) > var.maxLength:
            self.containerStack[0]['container'].bell()
            var.set(var.get()[0:var.maxLength])

    def _upperEntry(self, name):
        var = self.widgetManager.get(WIDGET_NAMES.Entry, name, group=WidgetManager.VARS)
        chars = var.get().upper()
        var.set(chars)

    def _lowerEntry(self, name):
        var = self.widgetManager.get(WIDGET_NAMES.Entry, name, group=WidgetManager.VARS)
        chars = var.get().lower()
        var.set(chars)

    def _entryIn(self, name):
        self._updateEntryDefault(name, "in")

    def _entryOut(self, name):
        self._updateEntryDefault(name, "out")

    def _updateEntryDefault(self, name, mode=None):
        var = self.widgetManager.get(WIDGET_NAMES.Entry, name, group=WidgetManager.VARS)
        entry = self.widgetManager.get(WIDGET_NAMES.Entry, name)

        # ignore this if no default to apply
        if entry.default == "":
            return

        # disable any limits
        if var.lc_id is not None:
            var.trace_vdelete('w', var.lc_id)
        if var.uc_id is not None:
            var.trace_vdelete('w', var.uc_id)
        if var.ml_id is not None:
            var.trace_vdelete('w', var.ml_id)

        # disable any auto completion
        if var.auto_id is not None:
            var.trace_vdelete('w', var.auto_id)

        current = self.widgetManager.get(WIDGET_NAMES.Entry, name, group=WidgetManager.VARS).get()

        # disable any change function
        with PauseCallFunction(False, var, False):

            # clear & remove default
            if mode == "set" or (mode in [ "in", "clear"] and entry.showingDefault):
                var.set("")
                entry.showingDefault = False
                entry.config(justify=entry.oldJustify)
                if not self.ttkFlag:
                    entry.config(foreground=entry.oldFg)
                else:
                    entry.configure(style=entry.oldFg)
                if entry.isSecret:
                    entry.config(show="*")
            elif mode == "out" and (current == "" or entry.showingDefault):
                if entry.isSecret:
                    entry.config(show="")
                var.set(entry.default)
                entry.config(justify='center')
                if not self.ttkFlag:
                    entry.config(foreground='grey')
                else:
                    entry.configure(style="DefaultText.TEntry")

                entry.showingDefault = True
            elif mode == "update" and entry.showingDefault:
                if entry.isSecret:
                    entry.config(show="")
                var.set(entry.default)

        # re-enable any limits
        if var.lc_id is not None:
            var.lc_id = var.trace('w', self.MAKE_FUNC(self._lowerEntry, name))
        if var.uc_id is not None:
            var.uc_id = var.trace('w', self.MAKE_FUNC(self._upperEntry, name))
        if var.ml_id is not None:
            var.ml_id = var.trace('w', self.MAKE_FUNC(self._limitEntry, name))

        # re-enable auto completion
        if var.auto_id is not None:
            var.auto_id = var.trace('w', entry.textChanged)

    def setEntryDefault(self, name, text="default"):
        entry = self.widgetManager.get(WIDGET_NAMES.Entry, name)
        self.widgetManager.get(WIDGET_NAMES.Entry, name, group=WidgetManager.VARS)

        # remember current settings - to return to
        if not hasattr(entry, "oldJustify"):
            entry.oldJustify = entry.cget('justify')
        if not hasattr(entry, "oldFg"):
            if not self.ttkFlag:
                entry.oldFg = entry.cget('foreground')
            else:
                entry.oldFg = entry.cget("style")

        # configure default stuff
        entry.default = text
        entry.DEFAULT_TEXT = text

        # only show new text if empty
        self._updateEntryDefault(name, "out")

        # bind commands to show/remove the default
        if hasattr(entry, "defaultInEvent"):
            entry.unbind(entry.defaultInEvent)
            entry.unbind(entry.defaultOutEvent)

        in_command = self.MAKE_FUNC(self._entryIn, name)
        out_command = self.MAKE_FUNC(self._entryOut, name)
        entry.defaultInEvent = entry.bind("<FocusIn>", in_command, add="+")
        entry.defaultOutEvent = entry.bind("<FocusOut>", out_command, add="+")

    def clearEntry(self, name, callFunction=True, setFocus=True):
        var = self.widgetManager.get(WIDGET_NAMES.Entry, name, group=WidgetManager.VARS)

        # now call function
        with PauseCallFunction(callFunction, var, False):
            var.set("")

        self._updateEntryDefault(name, mode="clear")
        if setFocus: self.setFocus(name)

    def clearAllEntries(self, callFunction=False):
        for entry in self.widgetManager.group(WIDGET_NAMES.Entry, group=WidgetManager.VARS):
            self.clearEntry(entry, callFunction=callFunction, setFocus=False)

    def setFocus(self, name):
        entry = self.widgetManager.get(WIDGET_NAMES.Entry, name)
        entry.focus_set()

    def getFocus(self):
        widg = self.topLevel.focus_get()
        return self.widgetManager.getName(widg)

####################################
## Functions to get widget details
####################################

    def _lookupValue(self, myDict, val):
        for name in myDict:
            if isinstance(myDict[name], type([])):  # array of cbs
                for rb in myDict[name]:
                    if rb == val:
                        return name
            else:
                if myDict[name] == val:
                    return name
        return None


#####################################
# FUNCTIONS for progress bars (meters)
#####################################

    def meter(self, title, value=None, *args, **kwargs):
        """ simpleGUI - adds, sets & gets meters all in one go """
        widgKind = WIDGET_NAMES.Meter
        kind = kwargs.pop("kind","'meter")
        fill = kwargs.pop("fill", None)
        text = kwargs.pop("text", None)

        try: self.widgetManager.verify(WIDGET_NAMES.Meter, title)
        except: # widget exists
            meter =  self.getMeter(title)
        else: # new widget
            kwargs = self._parsePos(kwargs.pop("pos", []), kwargs)
            if kind == "split": meter = self._addMeter(title, "SPLIT", **kwargs)
            elif kind == "dual":  meter = self._addMeter(title, "DUAL", **kwargs)
            else: meter = self._addMeter(title, "METER", **kwargs)

        if value is not None: self.setMeter(title, value, text=text)
        if fill is not None: self.setMeterFill(title, fill)

        if len(kwargs) > 0:
            self._configWidget(title, widgKind, **kwargs)

        return meter

    def _addMeter(self, name, kind="METER", row=None, column=0, colspan=0, rowspan=0, **kwargs):
        self.widgetManager.verify(WIDGET_NAMES.Meter, name)

        if kind == "SPLIT":
            meter = SplitMeter(self.getContainer(), font=self._getContainerProperty('labelFont'))
        elif kind == "DUAL":
            meter = DualMeter(self.getContainer(), font=self._getContainerProperty('labelFont'))
        else:
            meter = Meter(self.getContainer(), font=self._getContainerProperty('labelFont'))

        self.widgetManager.add(WIDGET_NAMES.Meter, name, meter)
        self._positionWidget(meter, row, column, colspan, rowspan)
        return meter

    def addMeter(self, name, row=None, column=0, colspan=0, rowspan=0):
        return self._addMeter(name, "METER", row, column, colspan, rowspan)

    def addSplitMeter(self, name, row=None, column=0, colspan=0, rowspan=0):
        return self._addMeter(name, "SPLIT", row, column, colspan, rowspan)

    def addDualMeter(self, name, row=None, column=0, colspan=0, rowspan=0):
        return self._addMeter(name, "DUAL", row, column, colspan, rowspan)

    # update the value of the specified meter
    # note: expects a value between 0 (-100 for split/dual) & 100
    def setMeter(self, name, value=0.0, text=None):
        item = self.widgetManager.get(WIDGET_NAMES.Meter, name)
        item.set(value, text)

    def getMeter(self, name):
        item = self.widgetManager.get(WIDGET_NAMES.Meter, name)
        return item.get()

    def getAllMeters(self):
        meters = {}
        for k in self.widgetManager.group(WIDGET_NAMES.Meter):
            meters[k] = self.getMeter(k)
        return meters

    # a single colour for meters, a list of 2 colours for splits & duals
    def setMeterFill(self, name, colour):
        item = self.widgetManager.get(WIDGET_NAMES.Meter, name)
        item.configure(fill=colour)

#####################################
# FUNCTIONS for seperators
#####################################

    def separator(self, *args, **kwargs):
        """ simpleGUI - adds horizontal/vertical separators """
        direction = kwargs.pop("direction", "horizontal").lower()
        kwargs = self._parsePos(kwargs.pop("pos", []), kwargs)

        if direction == "vertical":
            return self.addVerticalSeparator(*args, **kwargs)
        else:
            return self.addHorizontalSeparator(*args, **kwargs)

    def addHorizontalSeparator(self, row=None, column=0, colspan=0, rowspan=0, colour=None):
        return self._addSeparator("horizontal", row, column, colspan, rowspan, colour)

    def addVerticalSeparator(self, row=None, column=0, colspan=0, rowspan=0, colour=None):
        return self._addSeparator("vertical", row, column, colspan, rowspan, colour)

    def _addSeparator(self, orient, row=None, column=0, colspan=0, rowspan=0, colour=None):
        sep = self._makeSeparator()(self.getContainer(), orient)
        if colour is not None:
            sep.configure(fg=colour)
        self.widgetManager.log(WIDGET_NAMES.Separator, sep)
        self._positionWidget(sep, row, column, colspan, rowspan)
        return sep

#####################################
# FUNCTIONS for pie charts
#####################################
    def pie(self, title, value=None, *args, **kwargs):
        """ simpleGUI - adds, sets & gets pies all in one go """
        widgKind = WIDGET_NAMES.PieChart
        name = kwargs.pop("name", None)

        try: self.widgetManager.verify(widgKind, title)
        except: # widget exists
            if name is not None: self.setPieChart(title, name, value)
            pie = self.getPieChart(title)
        else: # new widget
            kwargs = self._parsePos(kwargs.pop("pos", []), kwargs)
            pie = self.addPieChart(title, value, *args, **kwargs)

        if len(kwargs) > 0:
            self._configWidget(title, widgKind, **kwargs)
        return pie

    def addPieChart(self, name, fracs, row=None, column=0, colspan=0, rowspan=0):
        self.widgetManager.verify(WIDGET_NAMES.PieChart, name)
        self._loadTooltip()
        pie = PieChart(self.getContainer(), fracs, self._getContainerBg())
        self.widgetManager.add(WIDGET_NAMES.PieChart, name, pie)
        self._positionWidget(pie, row, column, colspan, rowspan, sticky=None)
        return pie

    def setPieChart(self, title, name, value):
        pie = self.widgetManager.get(WIDGET_NAMES.PieChart, title)
        pie.setValue(name, value)

#####################################
# FUNCTIONS for toolbar
#####################################
    # adds a list of buttons along the top - like a tool bar...
    def addToolbarButton(self, name, func, findIcon=False):
        self.addToolbar([name], func, findIcon)

    def toolbar(self, names, funcs, **kwargs):
        """ simpleGUI - shortener for toolbar """
        icons = kwargs.pop('icons', kwargs.pop('findIcon', False))
        pinned = kwargs.pop('pinned', None)
        disabled = kwargs.pop('disabled', None)
        hidden = kwargs.pop('hidden', None)
        status = kwargs.pop('status', None)

        bg = kwargs.pop('bg', None)
        if bg is not None:
            self.setToolbarBg(bg)

        self.addToolbar(names, funcs, findIcon=icons is not False)

        # allow status and icon name to be passed in a list
        for x, n in enumerate(names):
            if icons is not None:
                try: self.setToolbarIcon(n, icons[x])
                except: pass
            if status is not None:
                try: self.setToolbarButtonDisabled(n, not status[x])
                except: pass

        if pinned is not None: self.setToolbarPinned(pinned=pinned)
        if disabled is not None: self.setToolbarDisabled(disabled=disabled)
        if hidden is True: self.hideToolbar()

    def addToolbar(self, names, funcs, findIcon=False, **kwargs):
        # hide the toolbarMin bar
        if self.tb.toolbarMin is not None:
            self.tb.toolbarMin.pack_forget()
        # make sure the toolbar is showing
        try:
            self.tb.pack_info()
        except:
            self.tb.location = self.containerStack[0]['container']
            self.tb.pack(before=self.tb.location, side=TOP, fill=X)
        if not self.tb.inUse:
            self.tb.inUse = True

        image = None
        singleFunc = self._checkFunc(names, funcs)
        if not isinstance(names, list):
            names = [names]

        for i in range(len(names)):
            t = names[i]
            if (t in self.widgetManager.group(WIDGET_NAMES.Toolbar)):
                raise Exception(
                    "Invalid toolbar button name: " +
                    t +
                    " already exists")

            if findIcon:
                # turn off warnings about PNGs
                with PauseLogger():
                    imgFile = os.path.join(self.icon_path, t.lower() + ".png")
                    try:
                        image = self._getImage(imgFile)
                    except Exception as e:
                        image = None

            if not self.ttkFlag:
                but = Button(self.tb)
                but.config(relief=FLAT, font=self._buttonFont)
                if gui.GET_PLATFORM() == gui.MAC and self.tb.BG_COLOR is not None:
                    but.config(highlightbackground=self.tb.BG_COLOR)
            else:
                but = ttk.Button(self.tb)
            self.widgetManager.add(WIDGET_NAMES.Toolbar, t, but)

            if singleFunc is not None:
                u = self.MAKE_FUNC(singleFunc, t)
            else:
                u = self.MAKE_FUNC(funcs[i], t)

            but.config(command=u)
            if image is not None:
                # works on Mac & Windows :)
                but.config(image=image)
                but.image = image
                if not self.ttkFlag:
                    but.config(justify=LEFT, compound=TOP)
                else:
                    but.config(style="Toolbar.TButton")
            else:
                but.config(text=t)
            but.pack(side=LEFT, padx=2, pady=2)
            but.tt_var = self._addTooltip(but, t.title(), True)
            but.DEFAULT_TEXT=t

    def _setPinBut(self):

        # only call this once
        if self.tb.pinBut is not None:
            return

        # try to get the icon, if none - then set but to None, and ignore from now on
        imgFile = os.path.join(self.icon_path, "pin.gif")
        try:
            imgObj = self._getImage(imgFile)
            if not self.ttkFlag:
                self.tb.pinBut = Label(self.tb)
                if self.tb.BG_COLOR is not None:
                    self.tb.pinBut.config(bg=self.tb.BG_COLOR)
            else:
                self.tb.pinBut = ttk.Label(self.tb)
                self.tb.pinBut.config(style="Toolbar.TLabel")
        except:
            return

        # if image found, then set up the label
        if self.tb.pinBut is not None:
            self.tb.pinBut.config(image=imgObj)#, compound=TOP, text="", justify=LEFT)
            self.tb.pinBut.image = imgObj  # keep a reference!
            self.tb.pinBut.pack(side=RIGHT, anchor=NE, padx=0, pady=0)

            if gui.GET_PLATFORM() == gui.MAC:
                self.tb.pinBut.config(cursor="pointinghand")
            elif gui.GET_PLATFORM() in [gui.WINDOWS, gui.LINUX]:
                self.tb.pinBut.config(cursor="hand2")

            self.tb.pinBut.eventId = self.tb.pinBut.bind("<Button-1>", self._toggletb)
            self._addTooltip(self.tb.pinBut, "Click here to pin/unpin the toolbar.", True)

    # called by pinBut, to toggle the pin status of the toolbar
    def _toggletb(self, event=None):
        self.setToolbarPinned(not self.tb.pinned)

    def setToolbarPinned(self, pinned=True):
        self.tb.pinned = pinned
        self._setPinBut()
        if not self.tb.pinned:
            if self.tb.pinBut is not None:
                try:
                    self.tb.pinBut.image = self._getImage(os.path.join(self.icon_path, "unpin.gif"))
                except:
                    pass
            self.tb.makeMinBar()
            self.tb._minToolbar()
        else:
            if self.tb.pinBut is not None:
                try:
                    self.tb.pinBut.image = self._getImage(os.path.join(self.icon_path, "pin.gif"))
                except:
                    pass
            self.tb._maxToolbar()

        if self.tb.pinBut is not None:
            self.tb.pinBut.config(image=self.tb.pinBut.image)

    def setToolbarIcon(self, name, icon):
        if (name not in self.widgetManager.group(WIDGET_NAMES.Toolbar)):
            raise Exception("Unknown toolbar name: " + name)
        imgFile = os.path.join(self.icon_path, icon.lower() + ".png")
        with PauseLogger():
            self.setToolbarImage(name, imgFile)
#        self.widgetManager.get(WIDGET_NAMES.Toolbar, name).tt_var.set(icon)

    def setToolbarBg(self, bg):
        self.tb.BG_COLOR = bg
        if not self.ttkFlag:
            self.tb.config(bg=self.tb.BG_COLOR)
            if gui.GET_PLATFORM() == gui.MAC:
                for name, val in self.widgetManager.group(WIDGET_NAMES.Toolbar).items():
                    val.config(highlightbackground=self.tb.BG_COLOR)
            # config the pin button if exists
            if self.tb.pinBut is not None:
                self.tb.pinBut.config(bg=self.tb.BG_COLOR)
        else:
            self.ttkStyle.configure("Toolbar.TFrame", background=self.tb.BG_COLOR)
            self.ttkStyle.configure("Toolbar.TLabel", background=self.tb.BG_COLOR)

    def setToolbarImage(self, name, imgFile):
        if (name not in self.widgetManager.group(WIDGET_NAMES.Toolbar)):
            raise Exception("Unknown toolbar name: " + name)
        image = self._getImage(imgFile)
        self.widgetManager.get(WIDGET_NAMES.Toolbar, name).config(image=image)
        self.widgetManager.get(WIDGET_NAMES.Toolbar, name).image = image

    def removeToolbarButton(self, name, hide=True):
        if (name not in self.widgetManager.group(WIDGET_NAMES.Toolbar)):
            raise Exception("Unknown toolbar name: " + name)
        self.widgetManager.get(WIDGET_NAMES.Toolbar, name).destroy()
        self.widgetManager.remove(WIDGET_NAMES.Toolbar, name)
        if hide:
            if len(self.widgetManager.group(WIDGET_NAMES.Toolbar)) == 0:
                self.tb.pack_forget()
                self.tb.inUse = False
            if self.tb.toolbarMin is not None:
                self.tb.toolbarMin.pack_forget()

    def removeToolbar(self, hide=True):
        while len(self.widgetManager.group(WIDGET_NAMES.Toolbar)) > 0:
            self.removeToolbarButton(list(self.widgetManager.group(WIDGET_NAMES.Toolbar))[0], hide)

    def setToolbarButtonEnabled(self, name):
        self.setToolbarButtonDisabled(name, False)

    def setToolbarButtonDisabled(self, name, disabled=True):
        if (name not in self.widgetManager.group(WIDGET_NAMES.Toolbar)):
            raise Exception("Unknown toolbar name: " + name)
        if disabled:
            self.widgetManager.get(WIDGET_NAMES.Toolbar, name).config(state=DISABLED)
        else:
            self.widgetManager.get(WIDGET_NAMES.Toolbar, name).config(state=NORMAL)

    def setToolbarEnabled(self):
        self.setToolbarDisabled(False)

    def setToolbarDisabled(self, disabled=True):
        for but in self.widgetManager.group(WIDGET_NAMES.Toolbar).keys():
            if disabled:
                self.widgetManager.get(WIDGET_NAMES.Toolbar, but).config(state=DISABLED)
            else:
                self.widgetManager.get(WIDGET_NAMES.Toolbar, but).config(state=NORMAL)

        if self.tb.pinBut is not None:
            if disabled:
                # this fails if not bound
                if self.tb.pinBut.eventId:
                    self.tb.pinBut.unbind("<Button-1>", self.tb.pinBut.eventId)
                self.tb.pinBut.eventId = None
                self._disableTooltip(self.tb.pinBut)
                self.tb.pinBut.config(cursor="")
            else:
                if gui.GET_PLATFORM() == gui.MAC:
                    self.tb.pinBut.config(cursor="pointinghand")
                elif gui.GET_PLATFORM() in [gui.WINDOWS, gui.LINUX]:
                    self.tb.pinBut.config(cursor="hand2")

                self.tb.pinBut.eventId = self.tb.pinBut.bind("<Button-1>", self._toggletb)
                self._enableTooltip(self.tb.pinBut)

    # functions to hide & show the toolbar
    def hideToolbar(self):
        self.tb.hide()

    def showToolbar(self):
        self.tb.show()

    # Method to get all inputs.
    def getAllInputs(self, **kwargs):
        """Get all values, merge & return as a single dictionary.

        :param kwargs: will be _appended_ to the input list.

        Note, empty pairs from each input is stripped, existing keys
        will not be overridden!
        """

        # used to stop removal of empty inputs
        includeEmptyInputs = kwargs.pop('includeEmptyInputs', False)

        # All available inputs.
        inputs = filter(None, [
                  self.getAllEntries(),
                  self.getAllOptionBoxes(),
                  self.getAllSpinBoxes(),
                  self.getAllListBoxes(),
                  self.getAllProperties(),
                  self.getAllCheckBoxes(),
                  self.getAllRadioButtons(),
                  self.getAllScales(),
                  self.getAllMeters(),
                  self.getAllDatePickers(),
                  kwargs,
        ])
        result = data = dict()
        for pairs in inputs:
            for key, val in pairs.items():
                # Try and strip values.
                try:
                    val = val.strip()
                except AttributeError:
                    pass
                try:
                    # Skip if value is empty or if key already exists.
                    if (not includeEmptyInputs and not val) or result[key]:
                        continue
                except KeyError:
                    pass
                result[key] = val
        return result

#####################################
# FUNCTIONS for menu bar
#####################################
    def _initMenu(self):
        # create a menu bar - only shows if populated
        if not self.hasMenu:
            #            self.topLevel.option_add('*tearOff', FALSE)
            self.hasMenu = True
            self.menuBar = Menu(self.topLevel)
            if self.platform == self.MAC:
                appmenu = Menu(self.menuBar, name='apple')
                self.menuBar.add_cascade(menu=appmenu)
                self.widgetManager.add(WIDGET_NAMES.Menu, "MAC_APP", appmenu)
            elif self.platform == self.WINDOWS:
                # sysMenu must be added last, otherwise other menus vanish
                sysMenu = Menu(self.menuBar, name='system', tearoff=False)
                self.widgetManager.add(WIDGET_NAMES.Menu, "WIN_SYS", sysMenu)

    # add a parent menu, for menu items
    def createMenu(self, title, tearable=False, showInBar=True):
        self.widgetManager.verify(WIDGET_NAMES.Menu, title)
        self._initMenu()

        if title == "WIN_SYS" and self.platform != self.WINDOWS:
            self.warn("The WIN_SYS menu is specific to Windows")
            return None

        if self.platform == self.MAC and tearable:
            self.warn("Tearable menus (%s) not supported on MAC", title)
            tearable = False
        theMenu = Menu(self.menuBar, tearoff=tearable)
        if showInBar:
            self.menuBar.add_cascade(label=title, menu=theMenu)
        self.widgetManager.add(WIDGET_NAMES.Menu, title, theMenu)
        return theMenu

    def createRightClickMenu(self, title, showInBar=False):
        men = self.createMenu(title, False, showInBar)
        men.bind("<FocusOut>", lambda e: men.unpost())
        return men

    def _bindRightClick(self, item, value):
        if self.platform in [self.WINDOWS, self.LINUX]:
            item.bind('<Button-3>', lambda e, menu=value: self._rightClick(e, menu))
        else:
            item.bind('<Button-2>', lambda e, menu=value: self._rightClick(e, menu))

    # add items to the named menu
    def addMenuItem(self, title, item, func=None, kind=None, shortcut=None, underline=-1, rb_id=None, createBinding=True):
        # set the initial menubar
        self._initMenu()

        # get or create an initial menu
        if title is not None:
            try:
                theMenu = self.widgetManager.get(WIDGET_NAMES.Menu, title)
            except:
                theMenu = self.createMenu(title)
                if theMenu is None:
                    gui.warn('Unable to create menu: %s', title)
                    return

        if underline > -1 and self.platform == self.MAC:
            gui.warn("Underlining menu items not available on MAC")

        if func is not None:
            func = self.MAKE_FUNC(func, item)

        acc = None

        if shortcut is not None:
            if kind == 'cb':
                f = lambda e: self._menuCheckButtonBind(title, item, func)
                binding = EventBinding(shortcut, f, self._getTopLevel(), menuBinding=True)
            else:
                binding = EventBinding(shortcut, func, self._getTopLevel(), menuBinding=True)

            try:
                self.widgetManager.add(WIDGET_NAMES.Bindings, binding.displayName, binding)
                if createBinding: binding.createBindings()
                acc = binding.displayName
            except ItemLookupError:
                raise ItemLookupError('Unable to bind menu ' + item + ' to ' + binding.displayName + ' - binding already exists')

        # now, let's create the actual menu item
        if item == "-" or kind == "separator":
            theMenu.add_separator()
        elif kind == "topLevel" or title is None:
            if self.platform == self.MAC:
                self.warn("Unable to make topLevel menus (%s) on Mac", item)
            else:
                self.menuBar.add_command(
                    label=item, command=func, accelerator=acc, underline=underline)
        elif kind == "rb":
            varName = title + "rb" + item
            newRb = False
            if (varName in self.widgetManager.group(WIDGET_NAMES.Menu, group=WidgetManager.VARS)):
                var = self.widgetManager.get(WIDGET_NAMES.Menu, varName, group=WidgetManager.VARS)
            else:
                newRb = True
                var = StringVar(self.topLevel)
                self.widgetManager.add(WIDGET_NAMES.Menu, varName, var, group=WidgetManager.VARS)
            theMenu.add_radiobutton(label=rb_id, command=func, variable=var, value=rb_id, accelerator=acc, underline=underline)
            if newRb:
                self.setMenuRadioButton(title, item, rb_id)
        elif kind == "cb":
            varName = title + "cb" + item
            self.widgetManager.verify(WIDGET_NAMES.Menu, varName, group=WidgetManager.VARS)
            var = BooleanVar(self.topLevel)
            var.set(False)
            self.widgetManager.add(WIDGET_NAMES.Menu, varName, var, group=WidgetManager.VARS)
            theMenu.add_checkbutton(label=item, command=func, variable=var, onvalue=True, offvalue=False, accelerator=acc, underline=underline)
        elif kind == "sub":
            self.widgetManager.verify(WIDGET_NAMES.Menu, item)
            subMenu = Menu(theMenu, tearoff=False)
            self.widgetManager.add(WIDGET_NAMES.Menu, item, subMenu)
            theMenu.add_cascade(label=item, menu=subMenu)
        else:
            theMenu.add_command(label=item, command=func, accelerator=acc, underline=underline)

    # used to wrap check button bindings, so can also toggle
    def _menuCheckButtonBind(self, title, item, func):
        self.setMenuCheckBox(title, item)
        func(item)

    #################
    # wrappers for other menu types

    def addMenuList(self, menuName, names, funcs):
        # deal with a dict_keys object - messy!!!!
        if not isinstance(names, list):
            names = list(names)

        # append some Nones, if it's a list and contains separators
        if funcs is not None:
            if not callable(funcs):
                seps = names.count("-")
                for i in range(seps):
                    funcs.append(None)
            singleFunc = self._checkFunc(names, funcs)

        # add menu items
        for t in names:
            if funcs is None:
                u = None
            elif singleFunc is not None:
                u = singleFunc
            else:
                u = funcs.pop(0)

            self.addMenuItem(menuName, t, u)

    def _prepareCopyAndPasteMenu(self, event, widget=None):
        if self.copyAndPaste.inUse:
            if event is not None:
                widget = event.widget
            self._changeMenuState(self.widgetManager.get(WIDGET_NAMES.Menu, "EDIT"), DISABLED, 'Disabling', 10)
            self.copyAndPaste.setUp(widget)
            if self.copyAndPaste.canCopy:
                self.enableMenuItem("EDIT", "Copy")
            if self.copyAndPaste.canCut:
                self.enableMenuItem("EDIT", "Cut")
            if self.copyAndPaste.canPaste:
                self.enableMenuItem("EDIT", "Paste")
                self.enableMenuItem("EDIT", "Clear Clipboard")
            if self.copyAndPaste.canSelect:
                self.enableMenuItem("EDIT", "Select All")
                self.enableMenuItem("EDIT", "Clear All")
            if self.copyAndPaste.canUndo:
                self.enableMenuItem("EDIT", "Undo")
            if self.copyAndPaste.canRedo:
                self.enableMenuItem("EDIT", "Redo")
            if self.copyAndPaste.canFont:
                self.enableMenuItem("EDIT", "Bold")
                self.enableMenuItem("EDIT", "Italic")
                self.enableMenuItem("EDIT", "Bold & Italic")
                self.enableMenuItem("EDIT", "Underline")
            return True
        else:
            return False

    # called when copy/paste menu items are clicked
    def _copyAndPasteHelper(self, menu):
        if menu == "Cut":
            self.copyAndPaste.cut()
        elif menu == "Copy":
            self.copyAndPaste.copy()
        elif menu == "Paste":
            self.copyAndPaste.paste()
        elif menu == "Select All":
            self.copyAndPaste.selectAll()
        elif menu == "Clear Clipboard":
            self.copyAndPaste.clearClipboard()
        elif menu == "Clear All":
            self.copyAndPaste.clearText()
        elif menu == "Undo":
            self.copyAndPaste.undo()
        elif menu == "Redo":
            self.copyAndPaste.redo()
        elif menu in ["BOLD", "ITALIC", "UNDERLINE", "BOLD_ITALIC"]:
            self.copyAndPaste.font("AJ_"+menu)

    # add a single entry for a menu
    def addSubMenu(self, menu, subMenu):
        self.addMenuItem(menu, subMenu, func=None, kind="sub")

    def addMenu(self, name, func, shortcut=None, underline=-1):
        self.addMenuItem(None, name, func=func, kind="topLevel", shortcut=shortcut, underline=underline)

    def addMenuSeparator(self, menu):
        self.addMenuItem(menu, "-")

    def addMenuCheckBox(self, menu, name, func=None, shortcut=None, underline=-1):
        self.addMenuItem(menu, name, func, "cb", shortcut, underline)

    def addMenuRadioButton(self, menu, name, value, func=None, shortcut=None, underline=-1):
        self.addMenuItem(menu, name, func, "rb", shortcut, underline, value)

    def menu(self, menu, name=None, func=None, **kwargs):
        # kind: menu, sub, button, sep, check/tick, radio
        kind = kwargs.pop('kind', 'button')
        group = kwargs.pop('group', None)
        shortcut = kwargs.pop('shortcut', None)
        underline = kwargs.pop('underline', -1)

        tear = kwargs.pop('tear', False)
        state = kwargs.pop('state', None)
        image = kwargs.pop('image', None)
        icon = kwargs.pop('icon', None)
        align = kwargs.pop('align', 'left')

        if kind == 'menu':
            self.createMenu(menu, tearable=tear, showInBar=True)
        elif kind.startswith('sub'):
            self.addSubMenu(menu, name)
        elif kind.startswith('radio') or group is not None:
            self.addMenuRadioButton(menu, group, value=name, func=func, shortcut=shortcut, underline=underline)
        elif kind == 'button':
            if name is None and func is not None:
                self.addMenu(menu, func=func, shortcut=shortcut, underline=underline)
            elif name is None:
                self.createMenu(menu, tearable=tear, showInBar=True)
            elif isinstance(name, (list, tuple)):
                self.addMenuList(menu, name, func)
            else:
                self.addMenuItem(menu, name, func=func, kind=None, shortcut=shortcut, underline=underline)
        elif kind.startswith('sep'):
            self.addMenuSeparator(menu)
        elif kind.startswith('check') or kind.startswith('tick'):
            self.addMenuCheckBox(menu, name, func=func, shortcut=shortcut, underline=underline)

        if state is not None:
            if kind == 'menu' or kind.startswith('sub'):
                if state == 'disabled': self.disableMenu(menu, name)
                elif state == 'enabled': self.enableMenu(menu, name)
            else:
                if state == 'disabled': self.disableMenuItem(menu, name)
                elif state == 'enabled': self.enableMenuItem(menu, name)

        if image is not None: self.setMenuImage(menu, name, image, align=align)
        if icon is not None: self.setMenuIcon(menu, name, icon, align=align)

    #################
    # wrappers for setters

    def _setMenu(self, menu, title, value, kind):
        title = menu + kind + title
        var = self.widgetManager.get(WIDGET_NAMES.Menu, title, group=WidgetManager.VARS)
        if kind == "rb":
            var.set(value)
        elif kind == "cb":
            if value is None:
                var.set(not var.get())
            else:
                var.set(value)

    def setMenuCheckBox(self, menu, name, value=None):
        self._setMenu(menu, name, value, "cb")

    def setMenuRadioButton(self, menu, name, value):
        self._setMenu(menu, name, value, "rb")

    # set align = "none" to remove text
    def setMenuImage(self, menu, title, image, align="left"):
        theMenu = self.widgetManager.get(WIDGET_NAMES.Menu, menu)
        imageObj = self._getImage(image)
        if 16 != imageObj.width() or imageObj.width() != imageObj.height():
            self.warn("Invalid image resolution for menu item %s (%s) - should be 16x16", title, image)
            #imageObj = imageObj.subsample(2,2)
        try: theMenu.entryconfigure(title, image=imageObj, compound=align)
        except TclError: gui.error("Unable to set image for menu item: %s, in menu: %s - item not found", title, menu)

    def setMenuIcon(self, menu, title, icon, align="left"):
        image = os.path.join(self.icon_path, icon.lower() + ".png")
        with PauseLogger():
            self.setMenuImage(menu, title, image, align)

    def disableMenubar(self):
        gui.trace('Disabling toplevel menubar')
        self._disableMenu(self.menuBar)

    def enableMenubar(self):
        gui.trace('Enabling toplevel menubar')
        self._enableMenu(self.menuBar)

    def disableMenu(self, title):
        gui.trace('Disabling submenu: %s', title)
        theMenu = self.widgetManager.get(WIDGET_NAMES.Menu, title)
        self._disableMenu(theMenu)

    def enableMenu(self, title):
        gui.trace('Enabling submenu: %s', title)
        theMenu = self.widgetManager.get(WIDGET_NAMES.Menu, title)
        self._enableMenu(theMenu)

    def disableMenuItem(self, title, item):
        theMenu = self.widgetManager.get(WIDGET_NAMES.Menu, title)
        try:
            gui.trace("Disabling menu item: %s, in menu: %s", item, title)
            self._changeMenuItemState(theMenu, item, DISABLED) 
        except TclError:
            gui.error("Unable to disable menu item: %s, in menu: %s - item not found", item, title)

    def enableMenuItem(self, title, item):
        theMenu = self.widgetManager.get(WIDGET_NAMES.Menu, title)
        try:
            gui.trace("Enabling menu item: %s, in menu: %s", item, title)
            self._changeMenuItemState(theMenu, item, NORMAL) 
        except TclError:
            gui.error("Unable to enable menu item: %s, in menu: %s - item not found", item, title)

    def _disableMenu(self, menu): self._changeMenuState(menu, DISABLED, 'Disabling')
    def _enableMenu(self, menu): self._changeMenuState(menu, NORMAL, 'Enabling')

    def _changeMenuState(self, menu, state, text, limit=None):
        # changes the specified menu object's state to the new state, using the specified text for logging
        numMenus = menu.index("end")
        if numMenus is not None: # MAC_APP (and others?) returns None
            for item in range(numMenus+1):
                # we can cut-off early if requested internally
                if limit is not None and limit == item:
                    break
                if menu.type(item) == 'cascade':
                    label = menu.entrycget(item, 'label')
                    if len(label) == 0: label = 'SPECIAL MENU'
                    gui.trace('%s submenu: %s', text, label)
                    subMenu = self.topLevel.nametowidget(menu.entrycget(item, 'menu'))
                    self._changeMenuState(subMenu, state, text)
                    menu.entryconfig(item, state=state)
                elif menu.type(item) in ['separator', 'tearoff']:
                    gui.trace('Skipping separator/tearoff')
                else:
                    label = menu.entrycget(item, 'label')
                    gui.trace('%s item: %s', text, label)
                    # use the position - if the label is a number it breaks...
                    self._changeMenuItemState(menu, item, state)

    def _changeMenuItemState(self, menu, item, state):
        menu.entryconfigure(item, state=state)
        bindingName = menu.entrycget(item, 'accelerator')
        if bindingName is not None and len(bindingName) > 0:
            self.widgetManager.get(WIDGET_NAMES.Bindings, bindingName).changeBindings(state)

    def renameMenu(self, title, newName):
        theMenu = self.widgetManager.get(WIDGET_NAMES.Menu, title)
        try:
            self.menuBar.entryconfigure(title, label=newName)
        except TclError:
            gui.error("Unable to rename menu: %s - item not found", title)

    def renameMenuItem(self, title, item, newName):
        theMenu = self.widgetManager.get(WIDGET_NAMES.Menu, title)
        try:
            theMenu.entryconfigure(item, label=newName)
        except TclError:
            gui.error("Unable to rename menu item: %s, in menu: %s - item not found", item, title)

    def deleteMenuItem(self, title, item):
        theMenu = self.widgetManager.get(WIDGET_NAMES.Menu, title)
        try:
            bindingName = theMenu.entrycget(item, 'accelerator')
            theMenu.delete(item)
            self.widgetManager.get(WIDGET_NAMES.Bindings, bindingName).removeBindings()
            self.widgetManager.remove(WIDGET_NAMES.Bindings, bindingName)
        except TclError:
            gui.error("Unable to delete menu item: %s, in menu: %s - item not found", item, title)

    #################
    # wrappers for getters

    def _getMenu(self, menu, title, kind):
        title = menu + kind + title
        var = self.widgetManager.get(WIDGET_NAMES.Menu, title, group=WidgetManager.VARS)
        if kind == 'rb': return var.get()
        else:
            if var.get(): return True
            else: return False

    def getMenuCheckBox(self, menu, title):
        return self._getMenu(menu, title, "cb")

    def getMenuRadioButton(self, menu, title):
        return self._getMenu(menu, title, "rb")

    #################
    # wrappers for platform specific menus

    # enables the preferences item in the app menu
    def addMenuPreferences(self, func):
        if self.platform == self.MAC:
            self._initMenu()
            u = self.MAKE_FUNC(func, "preferences")
            self.topLevel.createcommand('tk::mac::ShowPreferences', u)
        else:
            self.warn("The Preferences Menu is specific to Mac OSX")

    # MAC help menu
    def addMenuHelp(self, func):
        if self.platform == self.MAC:
            self._initMenu()
            helpMenu = Menu(self.menuBar, name='help')
            self.menuBar.add_cascade(menu=helpMenu, label='Help')
            u = self.MAKE_FUNC(func, "help")
            self.topLevel.createcommand('tk::mac::ShowHelp', u)
            self.widgetManager.add(WIDGET_NAMES.Menu, "MAC_HELP", helpMenu)
        else:
            self.warn("The Help Menu is specific to Mac OSX")

    # Shows a Window menu
    def addMenuWindow(self):
        if self.platform == self.MAC:
            self._initMenu()
            windowMenu = Menu(self.menuBar, name='window')
            self.menuBar.add_cascade(menu=windowMenu, label='Window')
            self.widgetManager.add(WIDGET_NAMES.Menu, "MAC_WIN", windowMenu)
        else:
            self.warn("The Window Menu is specific to Mac OSX")

    def disableMenuEdit(self):
        self.copyAndPaste.inUse = False

    # adds an edit menu - by default only as a pop-up
    # if inMenuBar is True - then show in menu too
    def addMenuEdit(self, inMenuBar=False):
        self._initMenu()
        self.copyAndPaste.inUse = True

        # in case we already made the menu - just return
        try: self.widgetManager.verify(WIDGET_NAMES.Menu, "EDIT")
        except: return

        editMenu = Menu(self.menuBar, tearoff=False)
        editMenu.bind("<FocusOut>", lambda e: editMenu.unpost())
        if inMenuBar:
            self.menuBar.add_cascade(menu=editMenu, label='Edit ')
        self.widgetManager.add(WIDGET_NAMES.Menu, "EDIT", editMenu)

        if gui.GET_PLATFORM() == gui.MAC:
            shortcut = "Command-"
        else:
            shortcut = "Control-"

        eList = [
            ('Cut', lambda e: self._copyAndPasteHelper("Cut"), "X", False),
            ('Copy', lambda e: self._copyAndPasteHelper("Copy"), "C", False),
            ('Paste', lambda e: self._copyAndPasteHelper("Paste"), "V", False),
            ('Select All', lambda e: self._copyAndPasteHelper("Select All"), "A", True if gui.GET_PLATFORM() == gui.MAC else False),
            ('Clear Clipboard', lambda e: self._copyAndPasteHelper("Clear Clipboard"), None, False)
            ]

        for (txt, cmd, sc, bind) in eList:
            acc = None if sc is None else shortcut + sc
            self.addMenuItem("EDIT", txt, cmd, shortcut=acc, createBinding=bind)

        # add a clear option
        self.addMenuSeparator("EDIT")
        self.addMenuItem("EDIT", "Clear All", lambda e: self._copyAndPasteHelper("Clear All"))

        self.addMenuSeparator("EDIT")
        self.addMenuItem("EDIT", 'Undo', lambda e: self._copyAndPasteHelper("Undo"), shortcut=shortcut + "Z", createBinding=False)
        self.addMenuItem("EDIT", 'Redo', lambda e: self._copyAndPasteHelper( "Redo"), shortcut=shortcut+"Shift-Z", createBinding=True)

        self.addMenuSeparator("EDIT")
        self.addMenuItem("EDIT", "Bold", lambda e: self._copyAndPasteHelper("BOLD"), shortcut=shortcut+"B")
        self.addMenuItem("EDIT", "Italic", lambda e: self._copyAndPasteHelper("ITALIC"), shortcut=shortcut+"I")
        self.addMenuItem("EDIT", "Underline", lambda e: self._copyAndPasteHelper("UNDERLINE"), shortcut=shortcut+"U")
        self.addMenuItem("EDIT", "Bold & Italic", lambda e: self._copyAndPasteHelper("BOLD_ITALIC"), shortcut=shortcut+"Shift-B")

        self.disableMenu("EDIT")

    def _editMenuSetter(self, enabled=True):
        if enabled:
            self.addMenuEdit()
        else:
            self.disableMenuEdit()

    def _editMenuGetter(self):
        return self.copyAndPaste.inUse

    editMenu = property(_editMenuGetter, _editMenuSetter)

    def appJarAbout(self, menu=None):
        self.infoBox("About appJar",
                        "---\n" +
                        __copyright__ + "\n" +
                        "---\n\t" +
                        gui.SHOW_VERSION().replace("\n", "\n\t") + "\n" +
                        "---\n" +
                        gui.SHOW_PATHS() + "\n" +
                        "---")

    def appJarHelp(self, menu=None):
        self.infoBox("appJar Help", "For help, visit " + __url__)

    def addAppJarMenu(self):
        if self.platform == self.MAC:
            self.addMenuItem("MAC_APP", "About appJar", self.appJarAbout)
            self.addMenuWindow()
            self.addMenuHelp(self.appJarHelp)
        elif self.platform == self.WINDOWS:
            self.addMenuSeparator('WIN_SYS')
            self.addMenuItem("WIN_SYS", "About appJar", self.appJarAbout)
            self.addMenuItem("WIN_SYS", "appJar Help", self.appJarHelp)

#####################################
# FUNCTIONS for status bar
#####################################

    def removeStatusbarField(self, field):
        if self.hasStatus and field < len(self._statusFields):
            self._statusFields[field].pack_forget()
            self._statusFields[field].destroy()
            del self._statusFields[field]
        else:
            raise ItemLookupError("Invalid field number for statusbar: " + str(field))

    def removeStatusbar(self):
        if self.hasStatus:
            while len(self._statusFields) > 0:
                self.removeStatusbarField(0)

            self.statusFrame.pack_forget()
            self.statusFrame.destroy()

            self.hasStatus = False
            self.header = ""

    def status(self, *args, **kwargs):
        self.statusbar(*args, **kwargs)

    def statusbar(self, *args, **kwargs):
        """ simpleGUI - shortener for statusbar """
        bg = kwargs.pop('bg', None)
        fg = kwargs.pop('fg', None)
        width = kwargs.pop('width', None)

        text = kwargs.pop('text', "")
        header = kwargs.pop('header', None)
        fields = kwargs.pop('fields', 1)
        field = kwargs.pop('field', 0)
        side = kwargs.pop('side', None)

        if not self.hasStatus:
            self.addStatusbar(header=header, fields=fields, side=side)
            self.setStatusbar(text=text)
        else:
            if len(args) > 0: text = args[0]
            if len(args) > 1: field = args[1]

            if header is not None: self.setStatusbarHeader(header)
            self.setStatusbar(text=text, field=field)

        if bg is not None: self.setStatusbarBg(bg)
        if fg is not None: self.setStatusbarFg(fg)
        if width is not None: self.setStatusbarWidth(width)

    def addStatusbar(self, header="", fields=1, side=None):
        if not self.hasStatus:
            class Statusbar(Frame, object):
                def __init__(self, master, **kwargs):
                    super(Statusbar, self).__init__(master, **kwargs)

            self.hasStatus = True
            self.header = header
            self.statusFrame = Statusbar(self.appWindow)
            self.statusFrame.config(bd=1, relief=SUNKEN)
            self.statusFrame.pack(side=BOTTOM, fill=X, anchor=S)

            self._statusFields = []
            for i in range(fields):
                self._statusFields.append(Label(self.statusFrame))
                self._statusFields[i].config(
                    bd=1,
                    relief=SUNKEN,
                    anchor=W,
                    font=self._statusFont,
                    width=10)
                self._addTooltip(self._statusFields[i], "Status bar", True)

                if side == "LEFT":
                    self._statusFields[i].pack(side=LEFT)
                elif side == "RIGHT":
                    self._statusFields[i].pack(side=RIGHT)
                else:
                    self._statusFields[i].pack(side=LEFT, expand=1, fill=BOTH)
        else:
            self.error("Statusbar already exists - ignoring")

    def setStatusbarHeader(self, header):
        if self.hasStatus:
            self.header = header

    def setStatusbar(self, text, field=0):
        if self.hasStatus:
            if field is None:
                for status in self._statusFields:
                    status.config(text=self._getFormatStatus(text))
            elif field >= 0 and field < len(self._statusFields):
                self._statusFields[field].config(text=self._getFormatStatus(text))
            else:
                raise Exception("Invalid status field: " + str(field) +
                                ". Must be between 0 and " + str(len(self._statusFields) - 1))

    def setStatusbarBg(self, colour, field=None):
        if self.hasStatus:
            if field is None:
                for status in self._statusFields:
                    status.config(background=colour)
            elif field >= 0 and field < len(self._statusFields):
                self._statusFields[field].config(background=colour)
            else:
                raise Exception("Invalid status field: " + str(field) +
                                ". Must be between 0 and " + str(len(self._statusFields) - 1))

    def setStatusbarFg(self, colour, field=None):
        if self.hasStatus:
            if field is None:
                for status in self._statusFields:
                    status.config(foreground=colour)
            elif field >= 0 and field < len(self._statusFields):
                self._statusFields[field].config(foreground=colour)
            else:
                raise Exception("Invalid status field: " + str(field) +
                                ". Must be between 0 and " + str(len(self._statusFields) - 1))

    def setStatusbarWidth(self, width, field=None):
        if self.hasStatus:
            if field is None:
                for status in self._statusFields:
                    status.config(width=width)
            elif field >= 0 and field < len(self._statusFields):
                self._statusFields[field].config(width=width)
            else:
                raise Exception("Invalid status field: " + str(field) +
                                ". Must be between 0 and " + str(len(self._statusFields) - 1))

    def clearStatusbar(self, field=None):
        if self.hasStatus:
            if field is None:
                for status in self._statusFields:
                    status.config(text=self._getFormatStatus(""))
            elif field >= 0 and field < len(self._statusFields):
                self._statusFields[field].config(text=self._getFormatStatus(""))
            else:
                raise Exception("Invalid status field: " + str(field) +
                                ". Must be between 0 and " + str(len(self._statusFields) - 1))

    # formats the string shown in the status bar
    def _getFormatStatus(self, text):
        text = str(text)
        if len(text) == 0:
            return ""
        elif self.header is None or len(self.header) == 0:
            return text
        else:
            return self.header + ": " + text

#####################################
# TOOLTIPS
#####################################

    def _addTooltip(self, item, text, hideWarn=False):
        self._loadTooltip()
        if not ToolTip:
            if not hideWarn:
                self.warn("ToolTips unavailable - check tooltip.py is in the lib folder")
        elif text == "":
            self._disableTooltip(item)
        else:
            # turn off warnings about tooltips
            with PauseLogger():
                # if there's already  tt, just change it
                if hasattr(item, "tt_var"):
                    item.tt_var.set(text)
                # otherwise create one
                else:
                    var = StringVar(self.topLevel)
                    var.set(text)
                    tip = ToolTip(item, delay=500, follow_mouse=1, textvariable=var)
                    item.tooltip = tip
                    item.tt_var = var

            return item.tt_var

    def _enableTooltip(self, item):
        if hasattr(item, "tooltip"):
            item.tooltip.configure(state="normal")
        else:
            self.warn("Unable to enable tooltip - none present.")

    def _disableTooltip(self, item):
        if hasattr(item, "tooltip"):
            item.tooltip.configure(state="disabled")
        else:
            self.warn("Unable to disable tooltip - none present.")

#####################################
# FUNCTIONS to show pop-up dialogs
#####################################

    def popUp(self, title, message=None, kind="info", parent=None):
        """ simpleGUI - shortener for the various popUps """

        if message is None:
            message = title
            title = kind.capitalize() + " Dialog"

        if kind == "info": return self.infoBox(title, message, parent)
        elif kind == "error": return self.errorBox(title, message, parent)
        elif kind == "warning": return self.warningBox(title, message, parent)
        elif kind == "yesno": return self.yesNoBox(title, message, parent)
        elif kind == "question": return self.questionBox(title, message, parent)
        elif kind == "ok": return self.okBox(title, message, parent)
        elif kind == "retry": return self.retryBox(title, message, parent)
        elif kind == "string": return self.stringBox(title, message, parent)
        elif kind == "integer": return self.integerBox(title, message, parent)
        elif kind == "float": return self.floatBox(title, message, parent)
        elif kind == "text": return self.textBox(title, message, parent)
        elif kind == "number": return self.numberBox(title, message, parent)
        else: gui.error("Invalid popUp kind: %s, with title: %s", kind, title)

    def prompt(self, title, message, kind="string", parent=None):
        return self.popUp(title, message, kind, parent)

    # function to access the last made pop_up
    def getPopUp(self):
        return self.topLevel.POP_UP

    def infoBox(self, title, message, parent=None):
        self.topLevel.update_idletasks()
        if parent is None:
            MessageBox.showinfo(title, message)
            if self.topLevel.displayed:
                self._bringToFront()
        else:
            parent = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent)
            opts = {"parent": parent}
            MessageBox.showinfo(title, message, **opts)
            self._bringToFront(parent)


    def errorBox(self, title, message, parent=None):
        self.topLevel.update_idletasks()
        if parent is None:
            MessageBox.showerror(title, message)
            if self.topLevel.displayed:
                self._bringToFront()
        else:
            parent = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent)
            opts = {"parent": parent}
            MessageBox.showerror(title, message, **opts)
            self._bringToFront(parent)

    def warningBox(self, title, message, parent=None):
        self.topLevel.update_idletasks()
        if parent is None:
            MessageBox.showwarning(title, message)
            if self.topLevel.displayed:
                self._bringToFront()
        else:
            parent = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent)
            opts = {"parent": parent}
            MessageBox.showwarning(title, message, **opts)
            self._bringToFront(parent)

    def yesNoBox(self, title, message, parent=None):
        self.topLevel.update_idletasks()
        if parent is None:
            return MessageBox.askyesno(title, message)
        else:
            parent = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent)
            opts = {"parent": parent}
            return MessageBox.askyesno(title=title, message=message, **opts)

    def stringBox(self, title, message, parent=None):
        self.topLevel.update_idletasks()
        if parent is None:
            return SimpleDialog.askstring(title, message)
        else:
            parent = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent)
            opts = {"parent": parent}
            return SimpleDialog.askstring(title=title, message=message, **opts)

    def integerBox(self, title, message, parent=None):
        self.topLevel.update_idletasks()
        if parent is None:
            return SimpleDialog.askinteger(title, message)
        else:
            parent = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent)
            opts = {"parent": parent}
            return SimpleDialog.askinteger(title=title, message=message, **opts)

    def floatBox(self, title, message, parent=None):
        self.topLevel.update_idletasks()
        if parent is None:
            return SimpleDialog.askfloat(title, message)
        else:
            parent = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent)
            opts = {"parent": parent}
            return SimpleDialog.askfloat(title=title, message=message, **opts)

    def questionBox(self, title, message, parent=None):
        self.topLevel.update_idletasks()
        if parent is None:
            return True if MessageBox.askquestion(title, message).lower() == "yes" else False
        else:
            parent = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent)
            opts = {"parent": parent}
            return True if MessageBox.askquestion(title, message, **opts).lower() == "yes" else False

    def okBox(self, title, message, parent=None):
        self.topLevel.update_idletasks()
        title, message = self._translatePopup(title, message)
        if parent is None:
            return MessageBox.askokcancel(title, message)
        else:
            parent = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent)
            opts = {"parent": parent}
            return MessageBox.askokcancel(title, message, **opts)

    def retryBox(self, title, message, parent=None):
        self.topLevel.update_idletasks()
        if parent is None:
            return MessageBox.askretrycancel(title, message)
        else:
            parent = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent)
            opts = {"parent": parent}
            return MessageBox.askretrycancel(title, message, **opts)

    def openBox(self, title=None, dirName=None, fileTypes=None, asFile=False, parent=None, multiple=False, mode='r'):

        self.topLevel.update_idletasks()

        # define options for opening
        options = {}

        if title is not None:
            options['title'] = title
        if dirName is not None:
            options['initialdir'] = dirName
        if fileTypes is not None:
            options['filetypes'] = fileTypes
        if parent is not None:
            options["parent"] = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent)

        if asFile:
            options["mode"] = mode
            if multiple: files = list(filedialog.askopenfiles(**options))
            else: files = filedialog.askopenfile(**options)

            return files
        # will return "" if cancelled
        else:
            if multiple: files = list(self.topLevel.tk.splitlist(filedialog.askopenfilenames(**options)))
            else: files = filedialog.askopenfilename(**options)

            return files

    def saveBox( self, title=None, fileName=None, dirName=None, fileExt=".txt",
            fileTypes=None, asFile=False, parent=None):
        self.topLevel.update_idletasks()
        if fileTypes is None:
            fileTypes = [('all files', '.*'), ('text files', '.txt')]
        # define options for opening
        options = {}
        options['defaultextension'] = fileExt
        options['filetypes'] = fileTypes
        options['initialdir'] = dirName
        options['initialfile'] = fileName
        options['title'] = title
        if parent is not None:
            options["parent"] = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent)

        if asFile:
            return filedialog.asksaveasfile(mode='w', **options)
        # will return "" if cancelled
        else:
            return filedialog.asksaveasfilename(**options)

    def directoryBox(self, title=None, dirName=None, parent=None):
        self.topLevel.update_idletasks()
        options = {}
        options['initialdir'] = dirName
        options['title'] = title
        options['mustexist'] = False
        if parent is not None:
            options["parent"] = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent)

        fileName = filedialog.askdirectory(**options)

        if fileName == "":
            return None
        else:
            return fileName

    def colourBox(self, colour='#ff0000', parent=None):
        self.topLevel.update_idletasks()
        if parent is None:
            col = askcolor(colour)
        else:
            parent = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent)
            opts = {"parent": parent}
            col = askcolor(colour, **opts)

        if col[1] is None:
            return None
        else:
            return col[1]

    def textBox(self, title="Text Box", question="Enter text", defaultValue=None, parent=None):
        self.topLevel.update_idletasks()
        if defaultValue is not None:
            defaultVar = StringVar(self.topLevel)
            defaultVar.set(defaultValue)
        else:
            defaultVar = None
        if parent is None:
            parent = self.topLevel
        else:
            parent = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent)

        return TextDialog(parent, title, question, defaultVar=defaultVar).result

    def numberBox(self, title="Number Box", question="Enter a number", parent=None):
        return self.numBox(title, question, parent)

    def numBox(self, title="Number Box", question="Enter a number", parent=None):
        self.topLevel.update_idletasks()
        if parent is None:
            parent = self.topLevel
        else:
            parent = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent)

        return NumDialog(parent, title, question).result

############################################################################
####     ******* ------ CLASS MAKERS FROM HERE ------ ***********  #########
############################################################################

    #####################################
    # Named classes for containing groups
    #####################################
    def _makeParentBox(self):
        class ParentBox(frameBase, object):

            def __init__(self, parent, **opts):
                super(ParentBox, self).__init__(parent, **opts)
                self.setup()

            def setup(self):
                pass

            # customised config setters
            def config(self, cnf=None, **kw):
                self.configure(cnf, **kw)

            def configure(self, cnf=None, **kw):
                # properties to propagate to CheckBoxes
                kw = gui.CLEAN_CONFIG_DICTIONARY(**kw)

                if "bg" in kw:
                    for child in self.winfo_children():
                        gui.SET_WIDGET_BG(child, kw["bg"])

                kw = self.processConfig(kw)

                # propagate anything left
                super(ParentBox, self).config(cnf, **kw)

            def processConfig(self, kw):
                return kw
        return ParentBox

    def _makeLabelBox(self):
        ParentBox = self._makeParentBox()
        class LabelBox(ParentBox):
            def setup(self):
                self.theLabel = None
                self.theWidget = None
        return LabelBox

    def _makeButtonBox(self):
        ParentBox = self._makeParentBox()
        class ButtonBox(ParentBox):
            def setup(self):
                self.theWidget = None
                self.theButton = None
        return ButtonBox

    def _makeWidgetBox(self):
        ParentBox = self._makeParentBox()
        class WidgetBox(ParentBox):
            def setup(self):
                self.theWidgets = []
        return WidgetBox

    def makeListBoxContainer(self):
        ParentBox = self._makeParentBox()
        class ListBoxContainer(Frame, object):

            def __init__(self, parent, **opts):
                super(ListBoxContainer, self).__init__(parent)

            # customised config setters
            def config(self, cnf=None, **kw):
                self.configure(cnf, **kw)

            def configure(self, cnf=None, **kw):
                # properties to propagate to CheckBoxes
                kw = gui.CLEAN_CONFIG_DICTIONARY(**kw)
                # propagate anything left
                super(ListBoxContainer, self).config(cnf, **kw)
        return ListBoxContainer

    #####################################
    # Simple Separator
    #####################################
    def _makeSeparator(self):
        class Separator(frameBase, object):

            def __init__(self, parent, orient="horizontal", *args, **options):
                super(Separator, self).__init__(parent, *args, **options)
                self.line = frameBase(self)
                self.line.SKIP_CLEANSE = True
                if orient == "horizontal":
                    self.line.config(relief="ridge", height=2, width=100, borderwidth=1)
                    self.line.pack(padx=5, pady=5, fill="x", expand=1)
                else:
                    self.line.config(relief="ridge", height=100, width=2, borderwidth=1)
                    self.line.pack(padx=5, pady=5, fill="y", expand=1)

            def config(self, cnf=None, **kw):
                self.configure(cnf, **kw)

            def configure(self, cnf=None, **kw):
                if "fg" in kw:
                    self.line.config(bg=kw.pop("fg"))

                super(Separator, self).config(cnf, **kw)

        return Separator

    #####################################
    # Drag Grip Label Class
    #####################################

    def _makeGrip(self):
        class Grip(labelBase, object):
            gray25 = BitmapImage(data="""
            #define im_width 16
            #define im_height 16
            static char im_bits[] = {
                0x88, 0x88, 0x22, 0x22, 0x88, 0x88, 0x22, 0x22,
                0x88, 0x88, 0x22, 0x22, 0x88, 0x88, 0x22, 0x22,
                0x88, 0x88, 0x22, 0x22, 0x88, 0x88, 0x22, 0x22,
                0x88, 0x88, 0x22, 0x22, 0x88, 0x88, 0x22, 0x22,
            };
            """)

            def __init__(self, *args, **kwargs):
                super(Grip, self).__init__(image=self.gray25, *args, **kwargs)
                self.config(cursor="fleur", anchor=CENTER)
                self.bind("<ButtonPress-1>", self.StartMove)
                self.bind("<ButtonRelease-1>", self.StopMove)
                self.bind("<B1-Motion>", self.OnMotion)

            def StartMove(self, event):
                self.x = event.x
                self.y = event.y

            def StopMove(self, event):
                self.x = None
                self.y = None

            def OnMotion(self, event):
                parent = self.winfo_toplevel()
                deltax = event.x - self.x
                deltay = event.y - self.y
                x = parent.winfo_x() + deltax
                y = parent.winfo_y() + deltay

                parent.geometry("+%s+%s" % (x, y))
        return Grip

    #####################################
    # Hyperlink Class
    #####################################
    @staticmethod
    def _makeLink():
        class Link(labelBase, object):

            def __init__(self, *args, **kwargs):
                self.useTtk = kwargs.pop('useTtk',False)
                super(Link, self).__init__(*args, **kwargs)
                self.fg = "#0000ff"
                self.overFg="#3366ff"

                if not self.useTtk:
                    self.config(fg=self.fg, takefocus=1)#, highlightthickness=0)
                else:
                    self.config(style="Link.TLabel")

                self.DEFAULT_TEXT = ""

                if gui.GET_PLATFORM() == gui.MAC:
                    self.config(cursor="pointinghand")
                elif gui.GET_PLATFORM() in [gui.WINDOWS, gui.LINUX]:
                    self.config(cursor="hand2")

                self.bind("<Enter>", self.enter)
                self.bind("<Leave>", self.leave)

            def enter(self, e):
                if self.useTtk:
                    self.config(style="LinkOver.TLabel")
                else:
                    super(Link, self).config(fg=self.overFg)

            def leave(self, e):
                if self.useTtk:
                    self.config(style="Over.TLabel")
                else:
                    super(Link, self).config(fg=self.fg)

            def registerCallback(self, callback):
                self.bind("<Button-1>", callback)
                self.bind("<Return>", callback)
                self.bind("<space>", callback)

            def launchBrowser(self, event):
                webbrowser.open_new(r"" + self.page)
                # webbrowser.open_new_tab(self.page)

            def registerWebpage(self, page):
                if not page.startswith("http"):
                    raise InvalidURLError("Invalid URL: " + page + " (it should begin as http://)")

                self.page = page
                self.bind("<Button-1>", self.launchBrowser)
                self.bind("<Return>", self.launchBrowser)
                self.bind("<space>", self.launchBrowser)

            def config(self, **kw):
                self.configure(**kw)

            def configure(self, **kw):
                kw = gui.CLEAN_CONFIG_DICTIONARY(**kw)
                if "text" in kw:
                    self.DEFAULT_TEXT = kw["text"]
                if 'fg' in kw:
                    self.fg = kw['fg']
                    self.overFg = gui.TINT(self, self.fg)

                super(Link, self).config(**kw)

            def cget(self, option):
                if option == "text" and hasattr(self, 'page'):
                    return self.page

                return super(Link, self).cget(option)

        return Link


    #######################
    # Upgraded scale - http://stackoverflow.com/questions/42843425/change-trough-increment-in-python-tkinter-scale-without-affecting-slider/
    #######################
    def _makeAjScale(self):
        class AjScale(scaleBase, object):
            '''a scale where a trough click jumps by a specified increment instead of the resolution'''
            def __init__(self, master=None, **kwargs):
                self.increment = kwargs.pop('increment',1)
                super(AjScale, self).__init__(master, **kwargs)
                self.bind('<Button-1>', self.jump)

            def jump(self, event):
                clicked = self.identify(event.x, event.y)
                return self._jump(clicked)

            def _jump(self, clicked):
                if clicked == 'trough1':
                    self.set(self.get() - self.increment)
                elif clicked == 'trough2':
                    self.set(self.get() + self.increment)
                else:
                    return None
                return 'break'

        return AjScale

    #####################################
    # appJar Frame
    #####################################

    def _makeAjFrame(self):
        class ajFrame(frameBase, object):
            def __init__(self, parent, *args, **options):
                super(ajFrame, self).__init__(parent, *args, **options)

        return ajFrame

    #########################
    # Class to provide auto-completion on Entry boxes
    # inspired by: https://gist.github.com/uroshekic/11078820
    #########################
    def _makeAutoCompleteEntry(self):
        ### Create the dynamic class
        class AutoCompleteEntry(entryBase, object):

            def __init__(self, words, tl, *args, **kwargs):
                super(AutoCompleteEntry, self).__init__(*args, **kwargs)
                self.allWords = words
                self.allWords.sort()
                self.topLevel = tl

                # store variable - so we can see when it changes
                self.var = self["textvariable"] = StringVar()
                self.var.auto_id = self.var.trace('w', self.textChanged)

                # register events
                self.bind("<Right>", self.selectWord)
                self.bind("<Return>", self.selectWord)
                self.bind("<Up>", self.moveUp)
                self.bind("<Down>", self.moveDown)
                self.bind("<FocusOut>", self.closeList, add="+")
                self.bind("<Escape>", self.closeList, add="+")

                # no list box - yet
                self.listBoxShowing = False
                self.rows = 10

            # customised config setters
            def config(self, cnf=None, **kw):
                self.configure(cnf, **kw)

            def configure(self, cnf=None, **kw):
                kw = gui.CLEAN_CONFIG_DICTIONARY(**kw)

                if "font" in kw:
                    self.listFont = kw["font"]

                # propagate anything left
                super(AutoCompleteEntry, self).config(cnf, **kw)

            def removeWord(self, word):
                if word in self.allWords:
                    self.allWords.remove(word)

            def addWords(self, words):
                if not hasattr(words, "__iter__"):
                    words = [words]
                for word in words:
                    if word not in self.allWords:
                        self.allWords.append(word)
                self.allWords.sort()

            def changeWords(self, words):
                self.allWords = words
                self.allWords.sort()

            def setNumRows(self, rows):
                self.rows = rows

            # function to see if words match
            def checkMatch(self, fieldValue, acListEntry):
                pattern = re.compile(re.escape(fieldValue) + '.*', re.IGNORECASE)
                return re.match(pattern, acListEntry)

            # function to get all matches as a list
            def getMatches(self):
                return [w for w in self.allWords if self.checkMatch(self.var.get(), w)]

            # called when typed in entry
            def textChanged(self, name, index, mode):
                # if no text - close list
                if self.var.get() == '':
                    self.closeList()
                else:
                    if not self.listBoxShowing:
                        self.makeListBox()
                    self.popListBox()

            # add words to the list
            def popListBox(self):
                if self.listBoxShowing:
                    self.listbox.delete(0, END)
                    shownWords = self.getMatches()
                    if shownWords:
                        for w in shownWords:
                            self.listbox.insert(END, w)
                        self.selectItem(0)

            # function to create & show an empty list box
            def makeListBox(self):
                self.listbox = Listbox(self.topLevel, width=self["width"]-8, height=8)
                self.listbox.config(height=self.rows)
#                self.listbox.config(bg=self.cget("bg"), selectbackground=self.cget("selectbackground"))
#                self.listbox.config(fg=self.cget("fg"))
                if hasattr(self, "listFont"):
                    self.listbox.config(font=self.listFont)
                self.listbox.bind("<Button-1>", self.mouseClickBox)
                self.listbox.bind("<Right>", self.selectWord)
                self.listbox.bind("<Return>", self.selectWord)

                x = self.winfo_rootx() - self.topLevel.winfo_rootx()
                y = self.winfo_rooty() - self.topLevel.winfo_rooty() + self.winfo_height()

                self.listbox.place(x=x, y=y)
                self.listBoxShowing = True

            # function to handle a mouse click in the list box
            def mouseClickBox(self, e=None):
                self.selectItem(self.listbox.nearest(e.y))
                self.selectWord(e)

            # function to close/delete list box
            def closeList(self, event=None):
                if self.listBoxShowing:
                    self.listbox.destroy()
                    self.listBoxShowing = False

            # copy word from list to entry, close list
            def selectWord(self, event):
                if self.listBoxShowing:
                    self.var.set(self.listbox.get(ACTIVE))
                    self.icursor(END)
                    self.closeList()
                return "break"

            # wrappers for up/down arrows
            def moveUp(self, event):
                return self.arrow("UP")

            def moveDown(self, event):
                return self.arrow("DOWN")

            # function for handling up/down keys
            def arrow(self, direction):
                if not self.listBoxShowing:
                    self.makeListBox()
                    self.popListBox()
                    curItem = 0
                    numItems = self.listbox.size()
                else:
                    numItems = self.listbox.size()
                    curItem = self.listbox.curselection()

                    if curItem == ():
                        curItem = -1
                    else:
                        curItem = int(curItem[0])

                    if direction == "UP" and curItem > 0:
                        curItem -= 1
                    elif direction == "UP" and curItem <= 0:
                        curItem = numItems - 1
                    elif direction == "DOWN" and curItem < numItems - 1:
                        curItem += 1
                    elif direction == "DOWN" and curItem == numItems - 1:
                        curItem = 0

                self.selectItem(curItem)

                # stop the event propgating
                return "break"

            # function to select the specified item
            def selectItem(self, position):
                numItems = self.listbox.size()
                self.listbox.selection_clear(0, numItems - 1)
                self.listbox.see(position)  # Scroll!
                self.listbox.selection_set(first=position)
                self.listbox.activate(position)

        # return the dynamic class
        return AutoCompleteEntry

    #####################################
    # Tree Widget Class
    # https://www.safaribooksonline.com/library/view/python-cookbook-2nd/0596007973/ch11s11.html
    # idlelib -> TreeWidget.py
    # https://svn.python.org/projects/python/trunk/Lib/idlelib/TreeWidget.py
    # modify minidom - https://wiki.python.org/moin/MiniDom
    #####################################
    def _makeAjTreeNode(self):
        class AjTreeNode(TreeNode, object):

            def __init__(self, canvas, parent, item):
                super(AjTreeNode, self).__init__(canvas, parent, item)

                self.hasAttr = False
                self.showAttr = False
                self.bgColour = None
                self.fgColour = None
                self.bgHColour = None
                self.fgHColour = None
                # called (if set) when a leaf is edited
                self.editEvent = None

                if self.parent:
                    self.bgColour = self.parent.bgColour
                    self.fgColour = self.parent.fgColour
                    self.bgHColour = self.parent.bgHColour
                    self.fgHColour = self.parent.fgHColour
                    self.editEvent = self.parent.editEvent
                    self.showAttr = self.parent.showAttr
                else:
                    # set this once, in parent
                    self.canvas.menu = None
                    self.canvas.lastSelected = None

                self.menuBound = False

            # customised config setters
            def config(self, cnf=None, **kw):
                self.configure(cnf, **kw)

            def configure(self, cnf=None, **kw):
                # properties to propagate to CheckBoxes
                kw = gui.CLEAN_CONFIG_DICTIONARY(**kw)

                if "bg" in kw:
                    self.setBgColour(kw.pop("bg"))
                if "fg" in kw:
                    self.setFgColour(kw.pop("fg"))

#                # propagate anything left
#                super(AjTreeNode, self).config(cnf, **kw)

            # NOT COMPLETE
            def addChild(self, child):
                child = self.__class__(self.canvas, self, child)
                self.children.append(child)
                self.update()

            def registerEditEvent(self, func):
                self.editEvent = func
                for c in self.children:
                    c.registerEditEvent(func)

            def showAttributes(self, show):
                self.showAttr = show
                for c in self.children:
                    c.showAttributes(show)
                self.update()

            def showMenu(self, show):
                if show:
                    if self.canvas.menu is None:
                        self.canvas.menu = Menu(self.canvas, tearoff=0)
                        self.canvas.menu.add_command(label="delete", command=self._delete)
                        self.canvas.menu.bind("<FocusOut>", lambda e: self.canvas.menu.unpost())
                    self._bindMenu()
                else:
                    # need to go through and unbind...
                    pass

            def setBgColour(self, colour):
                self.canvas.config(background=colour)
                self.bgColour = colour
                self._doUpdateColour()

            def setFgColour(self, colour):
                self.fgColour = colour
                self._doUpdateColour()

            def setBgHColour(self, colour):
                self.bgHColour = colour
                self._doUpdateColour()

            def setFgHColour(self, colour):
                self.fgHColour = colour
                self._doUpdateColour()

            def setAllColours(self, bg=None, fg=None, bgH=None, fgH=None):
                if bg is not None:
                    self.canvas.config(background=bg)
                    self.bgColour = bg
                if fg is not None: self.fgColour = fg
                if bgH is not None: self.bgHColour = bgH
                if fgH is not None: self.fgHColour = fgH
                self._doUpdateColour()

            def _doUpdateColour(self):
                self._updateColours(self.bgColour, self.bgHColour, self.fgColour, self.fgHColour)
                self.update()

            def _updateColours(self, bgCol, bgHCol, fgCol, fgHCol):
                self.bgColour = bgCol
                self.fgColour = fgCol
                self.bgHColour = bgHCol
                self.fgHColour = fgHCol
                for c in self.children:
                    c._updateColours(bgCol, bgHCol, fgCol, fgHCol)

            def draw(self, x, y):
                cy = super(AjTreeNode, self).draw(x, y)
                self._bindMenu()
                return cy

            # override parent function, so that we can change the label's background colour
            def drawtext(self):
                attr=self.item.node.attributes
                self.hasAttr = self.showAttr and attr is not None and len(attr) > 0

                if self.hasAttr:
                    self.attrId = self.canvas.create_text(self.x+20-1, self.y-1, anchor="nw", text='*')
                    self.x += 7
                super(AjTreeNode, self).drawtext()
                if self.hasAttr: self.x -= 7
                self.colourLabels()

                # add a tooltip for attributes
                if ToolTip is not False and self.hasAttr:
                    text = "Attributes\n"
                    for key, val in attr.items():
                        text += "  " + key + ":" + val + "\n"
                    text = text[:-1]
                    ToolTip(self.label, text, delay=500, follow_mouse=1)
                    ToolTip(self.canvas, text, specId=self.attrId, delay=500, follow_mouse=1)

            def _bindMenu(self):
                if self.canvas.menu is not None and not self.menuBound:
                    self.menuBound = True
                    if gui.GET_PLATFORM() in [gui.WINDOWS, gui.LINUX]:
                        self.canvas.tag_bind(self.image_id, "<Button-3>", self._showMenu)
                        if self.hasAttr: self.canvas.tag_bind(self.attrId, "<Button-3>", self._showMenu)
                        self.label.bind("<Button-3>", self._showMenu)
                    else:
                        self.canvas.tag_bind(self.image_id, "<Button-2>", self._showMenu)
                        if self.hasAttr: self.canvas.tag_bind(self.attrId, "<Button-2>", self._showMenu)
                        self.label.bind("<Button-2>", self._showMenu)

            # override parent function, so that we can change the label's background colour
            def drawicon(self):
                super(AjTreeNode, self).drawicon()

            def _showMenu(self, event=None):
                self.canvas.lastSelected = event.widget
                self.canvas.menu.focus_set()
                self.canvas.menu.post(event.x_root - 10, event.y_root - 10)
                return "break"

            def _delete(self):
                self.update()
                self.canvas.lastSelected.destroy()

            # override parent function, so that we can generate an event on finish editing
            def edit_finish(self, event=None):
                super(AjTreeNode, self).edit_finish(event)
                if self.editEvent is not None:
                    self.editEvent()

            def colourLabels(self):
                if self.showAttr and self.hasAttr:
                    self.canvas.itemconfigure(self.attrId, fill=self.fgColour)
                try:
                    if not self.selected:
                        self.label.config(background=self.bgColour, fg=self.fgColour)
                    else:
                        self.label.config(background=self.bgHColour, fg=self.fgHColour)
                except:
                    pass

            def getSelectedText(self):
                item = self.getSelected()
                if item is not None:
                    return item.GetText(), item.getAttribute()
                else:
                    return None

            def getSelected(self):
                if self.selected:
                    return self.item
                else:
                    for c in self.children:
                        val = c.getSelected()
                        if val is not None:
                            return val
                    return None

        return AjTreeNode

    def _makeAjTreeData(self):
        # implementation of container for XML data
        # functions implemented as specified in skeleton
        class AjTreeData(TreeItem, object):

            def __init__(self, document):
                # handle root node
                try: self.node = document.documentElement
                except AttributeError: self.node = document

                self.dblClickFunc = None
                self.clickFunc = None
                self.treeTitle = None
                self.canEdit = True

        # REQUIRED FUNCTIONS

            # called whenever the tree expands
            def GetText(self):
                node = self.node
                if node.nodeType == node.ELEMENT_NODE:
                    return node.nodeName
                elif node.nodeType == node.TEXT_NODE:
                    return node.nodeValue

            def getAttribute(self, att='id'):
                try: return self.node.attributes[att].value
                except: return None

            def IsEditable(self):
                return self.canEdit and not self.node.hasChildNodes()

            def SetText(self, text):
                self.node.replaceWholeText(text)

            def IsExpandable(self):
                return self.node.hasChildNodes()

            def GetIconName(self):
                if self.clickFunc is not None:
                    self.clickFunc(self.treeTitle, self.getAttribute())
                if not self.IsExpandable():
                    return "python"  # change to file icon

            def GetSubList(self):
                children = self.node.childNodes
                prelist = [AjTreeData(node) for node in children]
                itemList = [item for item in prelist if item.GetText().strip()]
                for item in itemList:
                    item.registerDblClick(self.treeTitle, self.dblClickFunc)
                    item.registerClick(self.treeTitle, self.clickFunc)
                    item.canEdit = self.canEdit
                return itemList

            def OnDoubleClick(self):
                if self.IsEditable():
                    # TO DO: start editing this node...
                    pass
                if self.dblClickFunc is not None:
                    self.dblClickFunc(self.treeTitle, self.getAttribute())

        #  EXTRA FUNCTIONS

            # TODO: can only set before calling go()
            def setCanEdit(self, value=True):
                self.canEdit = value

            # TODO: can only set before calling go()
            def registerDblClick(self, title, func):
                self.treeTitle = title
                self.dblClickFunc = func

            # TODO: can only set before calling go()
            def registerClick(self, title, func):
                self.treeTitle = title
                self.clickFunc = func

            # not used - for DEBUG
            def getSelected(self, spaces=1):
                if spaces == 1:
                    gui.trace("%s", self.node.tagName)
                for c in self.node.childNodes:
                    if gui.GET_WIDGET_CLASS(c) == "Element":
                        gui.trace("%s >> %s", " "*spaces, c.tagName)
                        node = AjTreeData(c)
                        node.getSelected(spaces + 2)
                    elif gui.GET_WIDGET_CLASS(c) == "Text":
                        val = c.data.strip()
                        if len(val) > 0:
                            gui.trace("%s >>>> %s", " "*spaces, val)
        return AjTreeData

Ancestors (in MRO)

  • gui
  • builtins.object

Class variables

var BASIC_NOTES

var DURATIONS

var E

var FLAT

var GROOVE

var LEFT

var LINUX

var MAC

var N

var NE

var NOTES

var NW

var RAISED

var RIDGE

var RIGHT

var S

var SE

var SUNKEN

var SW

var W

var WINDOWS

var bg

var built

var buttonFont

var colspan

var editMenu

var enterKey

var exe_file

var exe_path

var expand

var fastStop

var fg

var font

var fonts

var fullscreen

var guiPadding

var icon

var inPadding

var inputFont

var instantiated

var labelFont

var language

var lib_file

var lib_path

var location

var logFile

var logLevel

var padding

var randomColour

var resizable

var row

var rowspan

var size

var startFunction

var statusFont

var sticky

var stopFunction

var stretch

var title

var top

var transparency

var ttkTheme

var visible

Static methods

def __init__(

self, title=None, geom=None, handleArgs=True, language=None, startWindow=None, useTtk=False, useSettings=False, showIcon=True, **kwargs)

constructor - sets up the empty GUI window, and inits the various properties

def __init__(
                self, title=None, geom=None, handleArgs=True, language=None,
                startWindow=None, useTtk=False, useSettings=False, showIcon=True, **kwargs
            ):
    """ constructor - sets up the empty GUI window, and inits the various properties """
    if self.__class__.instantiated:
        raise Exception("You cannot have more than one instance of gui, try using a subWindow.")
    else:
        self.__class__.instantiated = True
    self.alive = True
    # first up, set the logger
    def _logForLevel(self, message, *args, **kwargs):
        if self.isEnabledFor(logging.DEBUG-5):
            self._log(logging.DEBUG-5, message, args, **kwargs)
    def _logToRoot(message, *args, **kwargs):
        logging.log(logging.DEBUG-5, message, *args, **kwargs)
    logging.basicConfig(level=logging.WARNING, format='%(asctime)s %(name)s:%(levelname)s %(message)s')
    logging.addLevelName(logging.DEBUG - 5, 'TRACE')
    setattr(logging, 'TRACE', logging.DEBUG -5)
    setattr(logging.getLoggerClass(), "trace", _logForLevel)
    setattr(logging, "trace", _logToRoot)
    logFile = kwargs.pop("file", kwargs.pop("logFile", None))
    logLevel = kwargs.pop("log", kwargs.pop("logLevel", None))
    self._language = language
    self.useSettings = useSettings
    self.settingsFile = "appJar.ini"
    self.externalSettings = {}
    self.startWindow = startWindow
    # check any command line arguments
    if argparse is None: handleArgs = False
    args = self._handleArgs() if handleArgs else None
    # warn if we're in an untested mode
    self._checkMode()
    # first out, verify the platform
    self.platform = gui.GET_PLATFORM()
    # process any command line arguments
    self.ttkFlag = False
    selectedTtkTheme = None
    if handleArgs:
        if args.f:
            gui.setLogFile(args.f)
            logFile = None # don't use any param logFile
        tmplevel, logLevel = logLevel, None
        if args.c: gui.setLogLevel("CRITICAL")
        elif args.e: gui.setLogLevel("ERROR")
        elif args.w: gui.setLogLevel("WARNING")
        elif args.i: gui.setLogLevel("INFO")
        elif args.d: gui.setLogLevel("DEBUG")
        elif args.t: gui.setLogLevel("TRACE")
        else: loglevel = tmplevel
    if logFile is not None: gui.setLogFile(logFile)
    if logLevel is not None: gui.setLogLevel(logLevel)
    if handleArgs:
        if args.l: self._language = args.l
        if args.ttk:
            useTtk = True
            if args.ttk is not True:
                selectedTtkTheme = args.ttk
        if args.s:
            self.useSettings = True
            if args.s is not True:
                self.settingsFile = args.s
    # configure as ttk
    if useTtk:
        self._useTtk()
        if useTtk is not True:
            selectedTtkTheme = useTtk
    # a stack to hold containers as being built
    # done here, as initArrays is called elsewhere - to reset the gubbins
    self.containerStack = []
    self.translations = {"POPUP":{}, "SOUND":{}, "EXTERNAL":{}}
    # first up, set up all the data stores
    self.widgetManager = WidgetManager()
    self.accessMade = False # accessibility subWindow
    self.splashConfig = None # splash screen?
    self.dnd = None # the dnd manager
    self.doFlash = False # set up flash variable
    self.hasTitleBar = True # used to hide/show title bar
    # validate function callbacks - used by numeric texts
    # created first time a widget is used
    self.validateNumeric = None
    self.validateSpinBox = None
    # dynamically create lots of functions for configuring stuff
    self._buildConfigFuncs()
    # language parser
    self.configParser = None
    # set up some default path locations
    # this fails if in interactive mode....
    try:
        gui.exe_file = str(os.path.basename(theMain.__file__))
        gui.exe_path = str(os.path.dirname(theMain.__file__))
    except:
        pass
    gui.lib_file = os.path.abspath(__file__)
    gui.lib_path = os.path.dirname(gui.lib_file)
    # location of appJar
    self.resource_path = os.path.join(gui.lib_path, "resources")
    self.icon_path = os.path.join(self.resource_path, "icons")
    self.sound_path = os.path.join(self.resource_path, "sounds")
    self.appJarIcon = os.path.join(self.icon_path, "favicon.ico")
    # user configurable
    self.userImages = gui.exe_path
    self.userSounds = gui.exe_path
    # create the main window - topLevel
    self.topLevel = Tk()
    self.topLevel.bind('<Configure>', self._windowEvent)
    def _setFocus(e):
        try: e.widget.focus_set()
        except: pass
    # these are specifically to make right-click menus disapear on linux
    self.topLevel.bind('<Button-1>', lambda e: _setFocus(e))
    self.topLevel.bind('<Button-2>', lambda e: _setFocus(e))
    self.topLevel.bind('<Button-3>', lambda e: _setFocus(e))
    # override close button
    self.topLevel.protocol("WM_DELETE_WINDOW", self.stop)
    # temporarily hide it
    self.topLevel.withdraw()
    # used to keep a handle on the last pop-up dialog
    # allows the dialog to be closed remotely
    # mainly for test-automation
    self.topLevel.POP_UP = None
    # create a frame to store all the widgets
    # now a canvas to allow animation...
    self.appWindow = CanvasDnd(self.topLevel)
    self.appWindow.pack(fill=BOTH, expand=True)
    self.topLevel.canvasPane = self.appWindow
    # set the windows title
    if title is None:
        title = "appJar" if gui.exe_file is None else gui.exe_file
    self.setTitle(title)
    self.topLevel.winIcon = None # will store the path to any icon
    # configure the geometry of the window
    self.topLevel.escapeBindId = None  # used to exit fullscreen
    self.topLevel.stopFunction = None  # used to exit fullscreen
    self.topLevel.startFunction = None
    # set the resize status - default to True
    self.topLevel.locationSet = False
    self.topLevel.ignoreSettings = False
    self.topLevel.isFullscreen = False # records if we're in fullscreen - stops hideTitle from breaking
    self.topLevel.displayed = True
    if geom is not None: self.setSize(geom)
    self.setResizable(True)
    self.Widgets = WIDGET_NAMES
    # 3 fonts used for most widgets
    self._buttonFont = tkFont.Font(family="Helvetica", size=12,)
    self._labelFont = tkFont.Font(family="Helvetica", size=12)
    self._inputFont = tkFont.Font(family="Helvetica", size=12)
    self._statusFont = tkFont.Font(family="Helvetica", size=12)
    # dedicated font for access widget
    self._accessFont = tkFont.Font(family="Arial", size=11,)
    # dedicated font for links - forces bold & underlined, but updated with label fonts
    self._linkFont = tkFont.Font(family="Helvetica", size=12, weight='bold', underline=1)
    self.tableFont = tkFont.Font(family="Helvetica", size=12)
    # create a menu bar - only shows if populated
    # now created in menu functions, as it generated a blank line...
    self.hasMenu = False
    self.hasStatus = False
    self.copyAndPaste = CopyAndPaste(self.topLevel, self)
    class Toolbar(frameBase, object):
        def __init__(self, master, **kwargs):
            super(Toolbar, self).__init__(master, **kwargs)
            self.BG_COLOR = None
            self.pinned = True
            self.pinBut = None
            self.inUse = False
            self.toolbarMin = None
            self.location = None
        def makeMinBar(self):
            if self.toolbarMin is None:
                self.toolbarMin = Frame(self.master, bd=1, relief=RAISED)
                self.toolbarMin.config(bg="gray", height=3)
                self.bind("<Leave>", self._minToolbar)
                self.toolbarMin.bind("<Enter>", self._maxToolbar)
        def hide(self):
            if self.inUse:
                self.pack_forget()
                if self.toolbarMin is not None:
                    self.toolbarMin.pack_forget()
        def show(self):
            if self.inUse:
                self.pack(before=self.location, side=TOP, fill=X)
                if self.toolbarMin is not None:
                    self.toolbarMin.pack_forget()
        def _minToolbar(self, e=None):
            if not self.pinned:
                if self.toolbarMin is not None:
                    self.toolbarMin.config(width=self.winfo_reqwidth())
                    self.toolbarMin.pack(before=self.location, side=TOP, fill=X)
                self.pack_forget()
        def _maxToolbar(self, e=None):
            self.pack(before=self.location, side=TOP, fill=X)
            if self.toolbarMin is not None:
                self.toolbarMin.pack_forget()
    class WidgetContainer(frameBase, object):
        def __init__(self, master, **kwargs):
            super(WidgetContainer, self).__init__(master, **kwargs)
    # create the main container for this GUI
    container = WidgetContainer(self.appWindow)
    # container = Label(self.appWindow) # made as a label, so we can set an
    # image
    if not self.ttkFlag:
        container.config(padx=2, pady=2, background=self.topLevel.cget("bg"))
    container.pack(fill=BOTH, expand=True)
    self._addContainer("root", WIDGET_NAMES.RootPage, container, 0, 1)
    self.tb = Toolbar(self.appWindow)
    if not self.ttkFlag:
        self.tb.config(bd=1, relief=RAISED)
    else:
        self.tb.config(style="Toolbar.TFrame")
    # set up the main container to be able to host an image
    self._configBg(container)
    if self.platform == self.WINDOWS and showIcon:
        try:
            self.setIcon(self.appJarIcon)
        except: # file not found
            gui.trace("Error setting Windows default icon")
    # set the ttk theme
    if self.ttkFlag:
        self.setTtkTheme(selectedTtkTheme)
    # for configuting event processing
    self.EVENT_SIZE = 1000
    self.EVENT_SPEED = 100
    self.preloadAnimatedImageId = None
    self.processQueueId = None
    # an array to hold any threaded events....
    self.events = []
    self.pollTime = 250
    self._fastStop = False
    self.configure(**kwargs)
    # special bindings
    self._globalBindings()
    self.built = True

def CENTER(

win, up=0)

@staticmethod
def CENTER(win, up=0):
    gui.SET_LOCATION("CENTER", win=win, up=up)

def CLEAN_CONFIG_DICTIONARY(

**kw)

Used by all Classes to tidy up dictionaries passed into config functions Allows us to more quickly process the dictionaries when overriding config

@staticmethod
def CLEAN_CONFIG_DICTIONARY(**kw):
    """ Used by all Classes to tidy up dictionaries passed into config functions
        Allows us to more quickly process the dictionaries when overriding config """
    try: kw['bg'] = kw.pop('background')
    except: pass
    try: kw['fg'] = kw.pop('foreground')
    except: pass
    kw = dict((k.lower().strip(), v) for k, v in kw.items())
    return kw

def GET_DIMS(

container)

returns a dictionary of dimensions for the supplied container

@staticmethod
def GET_DIMS(container):
    """ returns a dictionary of dimensions for the supplied container """
    container.update()
    dims = {}
    # get the apps requested width & height
    dims["r_width"] = container.winfo_reqwidth()
    dims["r_height"] = container.winfo_reqheight()
    # get the current width & height
    dims["w_width"] = container.winfo_width()
    dims["w_height"] = container.winfo_height()
    # get the window's width & height
    dims["s_width"] = container.winfo_screenwidth()
    dims["s_height"] = container.winfo_screenheight()
    # determine best geom for OS
    # on MAC & LINUX, w_width/w_height always 1 unless user-set
    # on WIN, w_height is bigger then r_height - leaving empty space
    if gui.GET_PLATFORM() in [gui.MAC, gui.LINUX]:
        if dims["w_width"] != 1:
            dims["b_width"] = dims["w_width"]
            dims["b_height"] = dims["w_height"]
        else:
            dims["b_width"] = dims["r_width"]
            dims["b_height"] = dims["r_height"]
    else:
        dims["b_height"] = max(dims["r_height"], dims["w_height"])
        dims["b_width"] = max(dims["r_width"], dims["w_width"])
    # GUI's corner - widget's corner
    # widget's corner can be 0 on windows when size not set by user
    dims["outerFrameWidth"] = 0 if container.winfo_x() == 0 else container.winfo_rootx() - container.winfo_x()
    dims["titleBarHeight"] = 0 if container.winfo_rooty() == 0 else container.winfo_rooty() - container.winfo_y()
    # add it all together
    dims["actualWidth"] = dims["b_width"] + (dims["outerFrameWidth"] * 2)
    dims["actualHeight"] = dims["b_height"] + dims["titleBarHeight"] + dims["outerFrameWidth"]
    dims["x"] = (dims["s_width"] // 2) - (dims["actualWidth"] // 2)
    dims["y"] = (dims["s_height"] // 2) - (dims["actualHeight"] // 2)
    return dims

def GET_PLATFORM(

)

returns one of the gui class's three static platform variables

@staticmethod
def GET_PLATFORM():
    """ returns one of the gui class's three static platform variables """
    if platform() in ["win32", "Windows"]:
        return gui.WINDOWS
    elif platform() == "Darwin":
        return gui.MAC
    elif platform() in ["Linux", "FreeBSD"]:
        return gui.LINUX
    else:
        raise Exception("Unknown platform: " + platform())

def GET_WIDGET_CLASS(

widget)

@staticmethod
def GET_WIDGET_CLASS(widget):
    return widget.__class__.__name__

def MAKE_FUNC(

funcName, param)

function to automate lambdas

@staticmethod
def MAKE_FUNC(funcName, param):
    ''' function to automate lambdas '''
    # make sure we get a function
    if not callable(funcName) and not hasattr(funcName, '__call__'):
        raise Exception("Invalid function: " + str(funcName))
    # check if the function requires arguments
    argsList = getArgs(funcName)
    # if no args, or 1 arg in a bound function
    noArgs = len(argsList[0])==0 or (len(argsList[0])==1 and inspect.ismethod(funcName))
    # if no args/varargs/kwargs then don't give the param
    if noArgs and argsList[1] is None and argsList[2] is None:
        return lambda *args: funcName()
    else:
        return lambda *args: funcName(param)

def MOUSE_POS_IN_WIDGET(

widget, event, findRoot=True)

returns the mouse's relative position in a widget :param widget: the widget to look in :param event: the event containing the mouse coordinates :param findRoot: if we should make this relative to the parent

@staticmethod
def MOUSE_POS_IN_WIDGET(widget, event, findRoot=True):
    """ returns the mouse's relative position in a widget
    :param widget: the widget to look in
    :param event: the event containing the mouse coordinates
    :param findRoot: if we should make this relative to the parent
    """
    # first we have to get the real master
    master = widget
    while findRoot:
        if isinstance(master, (SubWindow, Tk)):
            findRoot = False
        else:
            master = master.master
    # subtract the widget's top left corner from the root window's top corner
    x = event.x_root - master.winfo_rootx()
    y = event.y_root - master.winfo_rooty()
    gui.trace("<<MOUSE_POS_IN_WIDGET>> %s %s,%s", widget, x, y)
    return (x, y)

def PARSE_TWO_PARAMS(

x, y)

used to convert different possible x/y params to a tuple

@staticmethod
def PARSE_TWO_PARAMS(x, y):
    """ used to convert different possible x/y params to a tuple
    """
    if y is not None:
        return (x,y)
    else:
        if isinstance(x, (list, tuple)):
            return (x[0], x[1])
        else:
            if isinstance(x, UNIVERSAL_STRING):
                x=x.strip()
                if "," in x:
                    return [int(w.strip()) for w in x.split(",")]
            return (x, x)

def RANDOM_COLOUR(

self)

def RANDOM_COLOUR(self):
    return self.getRandomColour()

def SET_LOCATION(

x, y=None, ignoreSettings=None, win=None, up=0)

@staticmethod
def SET_LOCATION(x, y=None, ignoreSettings=None, win=None, up=0):
    if ignoreSettings is not None:
        win.ignoreSettings = ignoreSettings
    if gui.GET_PLATFORM() != gui.LINUX:
        trans = win.attributes('-alpha')
        win.attributes('-alpha', 0.0)
    win.update_idletasks()
    if isinstance(x, UNIVERSAL_STRING) and x.lower() in ['c', 'center', 'centre'] and y is None:
        x = y = 'c'
    else:
        x, y = gui.PARSE_TWO_PARAMS(x, y)
    gui.trace("Set location called with %s, %s", x, y)
    # get the window's dimensions
    dims = gui.GET_DIMS(win)
    # set any center positions
    if isinstance(x, UNIVERSAL_STRING) and x.lower() in ['c', 'center', 'centre']: x = dims["x"]
    if isinstance(y, UNIVERSAL_STRING) and y.lower() in ['c', 'center', 'centre']: y = dims["y"]
    # move the window up a bit if requested
    y = y - up if up < y else 0
    # fix any out of bounds positions
    if x < 0 or x > dims['s_width']: x = dims['x']
    if y < 0 or y > dims['s_height']: y = dims['y']
    gui.trace("Screen: %sx%s. Requested: %sx%s. Location: %s, %s",
                dims["s_width"], dims["s_height"], dims["b_width"],
                dims["b_height"], x, y)
    win.geometry("+%d+%d" % (x, y))
    win.locationSet = True
    if gui.GET_PLATFORM() != gui.LINUX:
        win.attributes('-alpha', trans)

def SET_WIDGET_BG(

widget, bg, external=False, tint=False)

@staticmethod
def SET_WIDGET_BG(widget, bg, external=False, tint=False):
    if bg is None: # ignore empty colours
        return
    widgType = gui.GET_WIDGET_CLASS(widget)
    isDarwin = gui.GET_PLATFORM() == gui.MAC
    isLinux = gui.GET_PLATFORM() == gui.LINUX
    gui.trace("Config %s BG to %s", widgType, bg)
    # these have a highlight border to remove
    hideBorders = [ "Text", "AjText",
        "ScrolledText", "AjScrolledText",
        "Scale", "AjScale",
        "OptionMenu",
        "Entry", "AutoCompleteEntry",
        "Radiobutton", "Checkbutton",
        "Button"]
    # these shouldn't have their BG coloured by default
    noBg = [ "Button",
        "Scale", "AjScale",
        "Spinbox", "Listbox", "OptionMenu",
        "SplitMeter", "DualMeter", "Meter",
        "Entry", "AutoCompleteEntry",
        "Text", "AjText",
        "ScrolledText", "AjScrolledText",
        "ToggleFrame"]
    # remove the highlight borders
    if widgType in hideBorders:
        if widgType == "Entry" and widget.isValidation:
            pass
        elif widgType == "OptionMenu":
            widget["menu"].config(borderwidth=0)
            widget.config(highlightbackground=bg)
            if isDarwin:
                widget.config(background=bg)
        elif widgType in ["Radiobutton", "Checkbutton"]:
            widget.config(activebackground=bg, highlightbackground=bg)
        else:
            widget.config(highlightbackground=bg)
    # do some fancy tinting
    if external or tint:
        if widgType in ["Button", "Scale", "AjScale"]:
            widget.config(activebackground=gui.TINT(widget, bg))
        elif widgType in ["Entry", "Text", "AjText", "ScrolledText", "AjScrolledText", "AutoCompleteEntry", "Spinbox"]:
            widget.config(selectbackground=gui.TINT(widget, bg))
            widget.config(highlightcolor=gui.TINT(widget, bg))
            if widgType in ["Text", "AjText", "ScrolledText", "AjScrolledText"]:
                widget.config(inactiveselectbackground=gui.TINT(widget, bg))
            elif widgType == "Spinbox":
                widget.config(buttonbackground=bg)
        elif widgType == "Listbox":
            widget.config(selectbackground=gui.TINT(widget, bg))
        elif widgType == "OptionMenu":
            widget.config(activebackground=gui.TINT(widget, bg))
            widget["menu"].config(activebackground=gui.TINT(widget, bg))
        elif widgType in ["Radiobutton", "Checkbutton"]:
            widget.config(activebackground=gui.TINT(widget, bg))
    # if this is forced - change everything
    if external:
        widget.config(bg=bg)
        if widgType == "OptionMenu":
            widget["menu"].config(bg=bg)
    # otherwise only colour un-excluded widgets
    elif widgType not in noBg:
        widget.config(bg=bg)
    # deal with flash labels
    if widgType == "Label":
        widget.origBg=bg
        try: widget.config(fg=widget.origFg)
        except: pass # not a flash label
    # now do any of the below containers
    if widgType in ["LabelFrame", "PanedFrame", "Pane", "ajFrame"]:
        for child in widget.winfo_children():
            gui.SET_WIDGET_BG(child, bg, external, tint)
    elif widgType == "LabelBox": # widget with label, in frame
        if widget.theLabel is not None:
            gui.SET_WIDGET_BG(widget.theLabel, bg, external, tint)
        gui.SET_WIDGET_BG(widget.theWidget, bg, external, tint)
    elif widgType == "ButtonBox": # widget with button, in frame
        gui.SET_WIDGET_BG(widget.theWidget, bg, external, tint)
        gui.SET_WIDGET_BG(widget.theButton, bg, external, tint)
    elif widgType == "ListBoxContainer": # list box container
        gui.SET_WIDGET_BG(widget.lb, bg, external, tint)
    elif widgType == "WidgetBox": # group of buttons or labels
        for widg in widget.theWidgets:
            gui.SET_WIDGET_BG(widg, bg, external, tint)

def SET_WIDGET_FG(

widget, fg, external=False)

@staticmethod
def SET_WIDGET_FG(widget, fg, external=False):
    widgType = gui.GET_WIDGET_CLASS(widget)
    gui.trace("SET_WIDGET_FG: %s - %s", widgType, fg)
    # only configure these widgets if external
    if widgType in ["Link", "Spinbox", "AjText", "AjScrolledText", "Button", "Entry", "AutoCompleteEntry"]:
        if external:
            try: # entry specific settings
                if not widget.showingDefault:
                    widget.oldFg = fg
                    widget.config(fg=fg)
                else:
                    widget.oldFg = fg
            except: # other widgets
                widget.config(fg=fg)
    # handle flash labels
    elif widgType == "Label":
        widget.config(fg=fg)
        widget.origFg=fg
        try: widget.config(bg=widget.origBg)
        except: pass # not a flash label
    elif widgType == "OptionMenu":
        if external:
            widget.config(fg=fg)
            widget["menu"].config(fg=fg)
    # deal with generic groupers
    elif widgType in ["Frame", "LabelFrame", "PanedFrame", "Pane", "ajFrame"]:
        for child in widget.winfo_children():
            gui.SET_WIDGET_FG(child, fg, external)
    # deal with specific containers
    elif widgType == "LabelBox":
        try:
            if not widget.isValidation:
                gui.SET_WIDGET_FG(widget.theLabel, fg, external)
        except Exception as e:
            gui.SET_WIDGET_FG(widget.theLabel, fg, external)
        gui.SET_WIDGET_FG(widget.theWidget, fg, external)
    elif widgType == "ButtonBox":
        gui.SET_WIDGET_FG(widget.theWidget, fg, external)
        gui.SET_WIDGET_FG(widget.theButton, fg, external)
    elif widgType == "WidgetBox":
        for child in widget.theWidgets:
            gui.SET_WIDGET_FG(child, fg, external)
    elif widgType == "ListBoxContainer":
        if external:
            gui.SET_WIDGET_FG(widget.lb, fg, external)
    # skip these widgets
    elif widgType in ["PieChart", "MicroBitSimulator", "Scrollbar"]:
        pass
    # always try these widgets
    else:
        try:
            widget.config(fg=fg)
        except Exception as e:
            pass

def SHOW_PATHS(

)

returns a printable string containing path to libraries, etc

@staticmethod
def SHOW_PATHS():
    """ returns a printable string containing path to libraries, etc """
    pathString = \
        "File Name: " + (gui.exe_file if gui.exe_file is not None else "") \
        + "\nFile Location: " + (gui.exe_path if gui.exe_path is not None else "") \
        + "\nLib Location: " + (gui.lib_path if gui.lib_path is not None else "")
    return pathString

def SHOW_VERSION(

)

returns a printable string containing version information

@staticmethod
def SHOW_VERSION():
    """ returns a printable string containing version information """
    verString = \
        "appJar: " + str(__version__) \
        + "\nPython: " + str(sys.version_info[0]) \
        + "." + str(sys.version_info[1]) + "." + str(sys.version_info[2]) \
        + "\nTCL: " + str(TclVersion) \
        + ", TK: " + str(TkVersion) \
        + "\nPlatform: " + str(platform()) \
        + "\npid: " + str(os.getpid()) \
        + "\nlocale: " + str(__locale__)
    return verString

def SPLIT_GEOM(

geom)

returns 2 lists made from the geom string :param geom: the geom string to parse :returns: a tuple containing a width/heiht tuple & a x/y position tuple

@staticmethod
def SPLIT_GEOM(geom):
    """ returns 2 lists made from the geom string
    :param geom: the geom string to parse
    :returns: a tuple containing a width/heiht tuple & a x/y position tuple
    """
    geom = geom.lower().split("x")
    width = int(float(geom[0]))
    height = int(float(geom[1].split("+")[0]))
    try:
        x = int(float(geom[1].split("+")[1]))
        y = int(float(geom[1].split("+")[2]))
    except IndexError:
        x = y = -1
    return (width, height), (x, y)

def TINT(

widget, colour)

@staticmethod
def TINT(widget, colour):
    col = []
    for a, b in enumerate(widget.winfo_rgb(colour)):
        t = int(min(max(0, b / 256 + (255 - b / 256) * .3), 255))
        t = str(hex(t))[2:]
        if len(t) == 1:
            t = '0' + t
        elif len(t) == 0:
            t = '00'
        col.append(t)
    if int(col[0], 16) > 210 and int(col[1], 16) > 210 and int(col[2], 16) > 210:
        if gui.GET_PLATFORM() == gui.LINUX:
            return "#c3c3c3"
        else:
            return "systemHighlight"
    else:
        return "#" + "".join(col)

def addAppJarMenu(

self)

def addAppJarMenu(self):
    if self.platform == self.MAC:
        self.addMenuItem("MAC_APP", "About appJar", self.appJarAbout)
        self.addMenuWindow()
        self.addMenuHelp(self.appJarHelp)
    elif self.platform == self.WINDOWS:
        self.addMenuSeparator('WIN_SYS')
        self.addMenuItem("WIN_SYS", "About appJar", self.appJarAbout)
        self.addMenuItem("WIN_SYS", "appJar Help", self.appJarHelp)

def addAutoEntry(

self, title, words, row=None, column=0, colspan=0, rowspan=0)

def addAutoEntry(self, title, words, row=None, column=0, colspan=0, rowspan=0):
    return self._entryMaker(title, row, column, colspan, rowspan, secret=False, label=False, kind="auto", words=words)

def addButton(

self, title, func, row=None, column=0, colspan=0, rowspan=0)

adds a button with the title as its text

def addButton(self, title, func, row=None, column=0, colspan=0, rowspan=0):
    ''' adds a button with the title as its text '''
    but = self._buildButton(title, func, self.getContainer())
    self._positionWidget(but, row, column, colspan, rowspan, None)
    return but

def addButtons(

self, names, funcs, row=None, column=0, colspan=0, rowspan=0, fill=False)

adds a 1D/2D list of buttons

def addButtons(self, names, funcs, row=None, column=0, colspan=0, rowspan=0, fill=False):
    ''' adds a 1D/2D list of buttons '''
    if not isinstance(names, list):
        raise Exception(
            "Invalid button: " +
            names +
            ". It must be a list of buttons.")
    singleFunc = self._checkFunc(names, funcs)
    frame = self._makeWidgetBox()(self.getContainer())
    if not self.ttk:
        frame.config(background=self._getContainerBg())
    # make them into a 2D array, if not already
    if not isinstance(names[0], list):
        names = [names]
        # won't be used if single func
        if funcs is not None:
            funcs = [funcs]
    sticky = None
    if fill: sticky=E+W
    for bRow in range(len(names)):
        for i in range(len(names[bRow])):
            t = names[bRow][i]
            if funcs is None:
                tempFunc = None
            elif singleFunc is None:
                tempFunc = funcs[bRow][i]
            else:
                tempFunc = singleFunc
            but = self._buildButton(t, tempFunc, frame)
            but.grid(row=bRow, column=i, sticky=sticky)
            Grid.columnconfigure(frame, i, weight=1)
            Grid.rowconfigure(frame, bRow, weight=1)
            frame.theWidgets.append(but)
    self._positionWidget(frame, row, column, colspan, rowspan)
    self.widgetManager.log(WIDGET_NAMES.FrameBox, frame)

def addCanvas(

self, title, row=None, column=0, colspan=0, rowspan=0)

adds a canvas at the specified position

def addCanvas(self, title, row=None, column=0, colspan=0, rowspan=0):
    ''' adds a canvas at the specified position '''
    self.widgetManager.verify(WIDGET_NAMES.Canvas, title)
    canvas = Canvas(self.getContainer())
    canvas.config(bd=0, highlightthickness=0)
    canvas.imageStore = []
    self._positionWidget(canvas, row, column, colspan, rowspan, "news")
    self.widgetManager.add(WIDGET_NAMES.Canvas, title, canvas)
    return canvas

def addCanvasCircle(

self, title, x, y, diameter, **kwargs)

adds a circle to the specified canvas

def addCanvasCircle(self, title, x, y, diameter, **kwargs):
    ''' adds a circle to the specified canvas '''
    return self.addCanvasOval(title, x, y, diameter, diameter, **kwargs)

def addCanvasImage(

self, title, x, y, image=<function gui.image at 0x104631d08>, **kwargs)

adds an image to the specified canvas

def addCanvasImage(self, title, x, y, image=image, **kwargs):
    ''' adds an image to the specified canvas '''
    canv = self.widgetManager.get(WIDGET_NAMES.Canvas, title)
    if isinstance(image, UNIVERSAL_STRING):
        image = self._getImage(image)
    canv.imageStore.append(image)
    return self.widgetManager.get(WIDGET_NAMES.Canvas, title).create_image(x, y, image=image, **kwargs)

def addCanvasLine(

self, title, x, y, x2, y2, **kwargs)

adds a line to the specified canvas

def addCanvasLine(self, title, x, y, x2, y2, **kwargs):
    ''' adds a line to the specified canvas '''
    return self.widgetManager.get(WIDGET_NAMES.Canvas, title).create_line(x, y, x2, y2, **kwargs)

def addCanvasOval(

self, title, x, y, xDiam, yDiam, **kwargs)

adds a oval to the specified canvas

def addCanvasOval(self, title, x, y, xDiam, yDiam, **kwargs):
    ''' adds a oval to the specified canvas '''
    return self.widgetManager.get(WIDGET_NAMES.Canvas, title).create_oval(x, y, x+xDiam, y+yDiam, **kwargs)

def addCanvasRectangle(

self, title, x, y, w, h, **kwargs)

adds a rectangle to the specified canvas

def addCanvasRectangle(self, title, x, y, w, h, **kwargs):
    ''' adds a rectangle to the specified canvas '''
    return self.widgetManager.get(WIDGET_NAMES.Canvas, title).create_rectangle(x, y, x+w, y+h, **kwargs)

def addCanvasText(

self, title, x, y, text=None, **kwargs)

adds text to the specified canvas

def addCanvasText(self, title, x, y, text=None, **kwargs):
    ''' adds text to the specified canvas '''
    return self.widgetManager.get(WIDGET_NAMES.Canvas, title).create_text(x, y, text=text, **kwargs)

def addCheckBox(

self, title, row=None, column=0, colspan=0, rowspan=0, name=None)

adds a new check box, at the specified position

def addCheckBox(self, title, row=None, column=0, colspan=0, rowspan=0, name=None):
    ''' adds a new check box, at the specified position '''
    self.widgetManager.verify(WIDGET_NAMES.CheckBox, title)
    var = IntVar(self.topLevel)
    if name is None:
        name = title
    if not self.ttkFlag:
        cb = Checkbutton(self.getContainer(), text=name, variable=var)
        cb.config(
            font=self._getContainerProperty('labelFont'),
            background=self._getContainerBg(),
            activebackground=self._getContainerBg(),
            anchor=W)
    else:
        cb = ttk.Checkbutton(self.getContainer(), text=name, variable=var)
    cb.DEFAULT_TEXT = name
    cb.bind("<Button-1>", self._grabFocus)
    self.widgetManager.add(WIDGET_NAMES.CheckBox, title, cb)
    self.widgetManager.add(WIDGET_NAMES.CheckBox, title, var, group=WidgetManager.VARS)
    self._positionWidget(cb, row, column, colspan, rowspan, EW)
    return cb

def addDatePicker(

self, name, row=None, column=0, colspan=0, rowspan=0)

adds a date picker at the specified position

def addDatePicker(self, name, row=None, column=0, colspan=0, rowspan=0):
    ''' adds a date picker at the specified position '''
    self.widgetManager.verify(WIDGET_NAMES.DatePicker, name)
    # initial DatePicker has these dates
    days = range(1, 32)
    self.MONTH_NAMES = calendar.month_name[1:]
    years = range(1970, 2021)
    # create a frame, and add the widgets
    frame = self.startFrame(name, row, column, colspan, rowspan)
    self.setExpand("none")
    self.addLabel(name + "_DP_DayLabel", "Day:", 0, 0)
    self.setLabelAlign(name + "_DP_DayLabel", "w")
    self.addOptionBox(name + "_DP_DayOptionBox", days, 0, 1)
    self.addLabel(name + "_DP_MonthLabel", "Month:", 1, 0)
    self.setLabelAlign(name + "_DP_MonthLabel", "w")
    self.addOptionBox(name + "_DP_MonthOptionBox", self.MONTH_NAMES, 1, 1)
    self.addLabel(name + "_DP_YearLabel", "Year:", 2, 0)
    self.setLabelAlign(name + "_DP_YearLabel", "w")
    self.addOptionBox(name + "_DP_YearOptionBox", years, 2, 1)
    self.setOptionBoxChangeFunction(
        name + "_DP_MonthOptionBox",
        self._updateDatePickerDays)
    self.setOptionBoxChangeFunction(
        name + "_DP_YearOptionBox",
        self._updateDatePickerDays)
    self.stopFrame()
    frame.isContainer = False
    self.widgetManager.add(WIDGET_NAMES.DatePicker, name, frame)

def addDbGrid(

self, title, db, table, row=None, column=0, colspan=0, rowspan=0, action=None, addRow=None, actionHeading='Action', actionButton='Press', addButton='Add', showMenu=False)

DEPRECATED - adds a new table widget, with the specified database and table

def addDbGrid(self, title, db, table, row=None, column=0, colspan=0, rowspan=0, action=None, addRow=None,
            actionHeading="Action", actionButton="Press", addButton="Add", showMenu=False):
    ''' DEPRECATED - adds a new table widget, with the specified database and table '''
    gui.warn("Deprecated - grids renamed to tables")
    return self.addDbTable(title, db, table, row, column, colspan, rowspan, action, addRow, actionHeading, actionButton, addButton, showMenu)

def addDbOptionBox(

self, title, db, row=None, column=0, colspan=0, rowspan=0, **kwargs)

adds an option box, with a list of tables form the specified database

def addDbOptionBox(self, title, db, row=None, column=0, colspan=0, rowspan=0, **kwargs):
    ''' adds an option box, with a list of tables form the specified database '''
    data = self._getDbTables(db)
    opt = self.option(title, data, row, column, colspan, rowspan, **kwargs)
    opt.db = db
    return opt

def addDbTable(

self, title, value, table, row=None, column=0, colspan=0, rowspan=0, action=None, addRow=None, actionHeading='Action', actionButton='Press', addButton='Add', showMenu=False, border='solid', **kwargs)

creates a new Table, displaying the specified database & table

def addDbTable(self, title, value, table, row=None, column=0, colspan=0, rowspan=0,
            action=None, addRow=None, actionHeading="Action", actionButton="Press",
            addButton="Add", showMenu=False, border="solid", **kwargs):
    ''' creates a new Table, displaying the specified database & table '''
    horiz=kwargs.pop('horizontal', True)
    self._importSqlite3()
    if not sqlite3:
        self.error("Unable to load DB data - can't load sqlite3")
        return
    with sqlite3.connect(value) as conn:
        cursor = conn.cursor()
        dataQuery = 'SELECT * from ' + table
        # select all data
        cursor.execute(dataQuery)
        grid = self.addTable(title, cursor, row, column, colspan, rowspan,
                    action, addRow, actionHeading, actionButton,
                    addButton, showMenu, border=border, horizontal=horiz
                )
    grid.db = value
    grid.dbTable = table
    return grid

def addDirectoryEntry(

self, title, row=None, column=0, colspan=0, rowspan=0)

def addDirectoryEntry(self, title, row=None, column=0, colspan=0, rowspan=0):
    return self._entryMaker(title, row, column, colspan, rowspan, secret=False, label=False, kind="directory")

def addDualMeter(

self, name, row=None, column=0, colspan=0, rowspan=0)

def addDualMeter(self, name, row=None, column=0, colspan=0, rowspan=0):
    return self._addMeter(name, "DUAL", row, column, colspan, rowspan)

def addEmptyLabel(

self, title, row=None, column=0, colspan=0, rowspan=0)

adds an empty label

def addEmptyLabel(self, title, row=None, column=0, colspan=0, rowspan=0):
    ''' adds an empty label '''
    return self.addLabel(title=title, text='', row=row, column=column, colspan=colspan, rowspan=rowspan)

def addEmptyMessage(

self, title, row=None, column=0, colspan=0, rowspan=0)

adds an empty message box

def addEmptyMessage(self, title, row=None, column=0, colspan=0, rowspan=0):
    ''' adds an empty message box '''
    return self.addMessage(title, "", row, column, colspan, rowspan)

def addEntry(

self, title, row=None, column=0, colspan=0, rowspan=0, secret=False)

adds an entry box for capturing text

def addEntry(self, title, row=None, column=0, colspan=0, rowspan=0, secret=False):
    ''' adds an entry box for capturing text '''
    return self._entryMaker(title, row, column, colspan, rowspan, secret=secret, label=False, kind="standard")

def addFileEntry(

self, title, row=None, column=0, colspan=0, rowspan=0)

adds an entry box with a button, that pops-up a file dialog

def addFileEntry(self, title, row=None, column=0, colspan=0, rowspan=0):
    ''' adds an entry box with a button, that pops-up a file dialog '''
    return self._entryMaker(title, row, column, colspan, rowspan, secret=False, label=False, kind="file")

def addFlashLabel(

self, title, text=None, row=None, column=0, colspan=0, rowspan=0)

adds a label with flashing text

def addFlashLabel(self, title, text=None, row=None, column=0, colspan=0, rowspan=0):
    ''' adds a label with flashing text '''
    lab = self.addLabel(title, text, row, column, colspan, rowspan)
    self.widgetManager.log(WIDGET_NAMES.FlashLabel, lab)
    self.doFlash = True
    return lab

def addGoogleMap(

self, title, row=None, column=0, colspan=0, rowspan=0)

adds a GoogleMap widget at the specified position

def addGoogleMap(self, title, row=None, column=0, colspan=0, rowspan=0):
    ''' adds a GoogleMap widget at the specified position '''
    self._loadURL()
    self._loadTooltip()
    if urlencode is False:
        raise Exception("Unable to load GoogleMaps - urlencode library not available")
    self.widgetManager.verify(WIDGET_NAMES.Map, title)
    gMap = GoogleMap(self.getContainer(), self, useTtk = self.ttkFlag, font=self._getContainerProperty('labelFont'))
    self._positionWidget(gMap, row, column, colspan, rowspan)
    self.widgetManager.add(WIDGET_NAMES.Map, title, gMap)
    return gMap

def addGrid(

self, title, data, row=None, column=0, colspan=0, rowspan=0, action=None, addRow=None, actionHeading='Action', actionButton='Press', addButton='Add', showMenu=False)

DEPRECATED - adds a new grid widget with the specified data

def addGrid(self, title, data, row=None, column=0, colspan=0, rowspan=0, action=None, addRow=None,
            actionHeading="Action", actionButton="Press", addButton="Add", showMenu=False):
    ''' DEPRECATED - adds a new grid widget with the specified data '''
    gui.warn("Deprecated - grids renamed to tables")
    return self.addTable(title, data, row, column, colspan, rowspan, action, addRow, actionHeading, actionButton, addButton, showMenu)

def addGridColumn(

self, title, columnNumber, data)

DEPRECATED - adds a column of data to the specified grid

def addGridColumn(self, title, columnNumber, data):
    ''' DEPRECATED - adds a column of data to the specified grid '''
    return self.addTableColumn(title, columnNumber, data)

def addGridRow(

self, title, data)

DEPRECATED - adds a row of data to the specified grid

def addGridRow(self, title, data):
    ''' DEPRECATED - adds a row of data to the specified grid '''
    return self.addTableRow(title, data)

def addGridRows(

self, title, data)

DEPRECATED - adds new rows of data to the specified grid

def addGridRows(self, title, data):
    ''' DEPRECATED - adds new rows of data to the specified grid '''
    return self.addTableRows(title, data)

def addGrip(

self, row=None, column=0, colspan=0, rowspan=0)

adds a grip, for dragging the GUI around

def addGrip(self, row=None, column=0, colspan=0, rowspan=0):
    ''' adds a grip, for dragging the GUI around '''
    grip = self._makeGrip()(self.getContainer())
    self._positionWidget(grip, row, column, colspan, rowspan)
    self._addTooltip(grip, "Drag here to move", True)
    return grip

def addHorizontalSeparator(

self, row=None, column=0, colspan=0, rowspan=0, colour=None)

def addHorizontalSeparator(self, row=None, column=0, colspan=0, rowspan=0, colour=None):
    return self._addSeparator("horizontal", row, column, colspan, rowspan, colour)

def addIcon(

self, name, iconName, row=None, column=0, colspan=0, rowspan=0, compound=None)

adds one of the built-in icons at the specified position

def addIcon(self, name, iconName, row=None, column=0, colspan=0, rowspan=0, compound=None):
    ''' adds one of the built-in  icons at the specified position '''
    icon = os.path.join(self.icon_path, iconName.lower()+".png")
    with PauseLogger():
        return self.addImage(name, icon, row, column, colspan, rowspan, compound=compound)

def addIconButton(

self, title, func, iconName, row=None, column=0, colspan=0, rowspan=0, align=None)

adds a button displaying the specified icon

def addIconButton(self, title, func, iconName, row=None, column=0, colspan=0, rowspan=0, align=None):
    ''' adds a button displaying the specified icon '''
    icon = os.path.join(self.icon_path, iconName.lower()+".png")
    with PauseLogger():
        return self.addImageButton(title, func, icon, row, column, colspan, rowspan, align)

def addImage(

self, name, imageFile, row=None, column=0, colspan=0, rowspan=0, compound=None)

Adds an image at the specified position

def addImage(self, name, imageFile, row=None, column=0, colspan=0, rowspan=0, compound=None):
    ''' Adds an image at the specified position '''
    self.widgetManager.verify(WIDGET_NAMES.Image, name)
    imgObj = self._getImage(imageFile)
    self._addImageObj(name, imgObj, row, column, colspan, rowspan, compound=compound)
    self.widgetManager.get(WIDGET_NAMES.Image, name).hasMouseOver = False
    return imgObj

def addImageButton(

self, title, func, imgFile, row=None, column=0, colspan=0, rowspan=0, align=None)

adds a button, displaying the specified image file

def addImageButton(self, title, func, imgFile, row=None, column=0, colspan=0, rowspan=0, align=None):
    ''' adds a button, displaying the specified image file '''
    but = self._buildButton(title, func, self.getContainer())
    self._positionWidget(but, row, column, colspan, rowspan, None)
    self.setButtonImage(title, imgFile, align)
    return but

def addImageData(

self, name, imageData, row=None, column=0, colspan=0, rowspan=0, fmt='gif', compound=None)

load image from base-64 encoded GIF use base64 module to convert binary data to base64

def addImageData(self, name, imageData, row=None, column=0, colspan=0, rowspan=0, fmt="gif", compound=None):
    ''' load image from base-64 encoded GIF
        use base64 module to convert binary data to base64 '''
    self.widgetManager.verify(WIDGET_NAMES.Image, name)
    imgObj = self._getImageData(imageData, fmt)
    self._addImageObj(name, imgObj, row, column, colspan, rowspan, compound=compound)
    self.widgetManager.get(WIDGET_NAMES.Image, name).hasMouseOver = False
    return imgObj

def addLabel(

self, title, text=None, row=None, column=0, colspan=0, rowspan=0, selectable=False)

Add a label to the GUI. :param title: a unique identifier for the Label :param text: optional text for the Label :param row/column/colspan/rowspan: the row/column to position the label in & how many rows/columns to strecth across :raises ItemLookupError: raised if the title is not unique

def addLabel(self, title, text=None, row=None, column=0, colspan=0, rowspan=0, selectable=False):
    """Add a label to the GUI.
    :param title: a unique identifier for the Label
    :param text: optional text for the Label
    :param row/column/colspan/rowspan: the row/column to position the label in & how many rows/columns to strecth across
    :raises ItemLookupError: raised if the title is not unique
    """
    self.widgetManager.verify(WIDGET_NAMES.Label, title)
    if text is None:
        gui.trace("Not specifying text for labels (%s) now uses the title for the text. If you want an empty label, pass an empty string ''", title)
        text = title
    if not selectable:
        if not self.ttkFlag:
            lab = Label(self.getContainer(), text=text)
            lab.config(justify=LEFT, font=self._getContainerProperty('labelFont'), background=self._getContainerBg())
            lab.origBg = self._getContainerBg()
        else:
            lab = ttk.Label(self.getContainer(), text=text)
    else:
        lab = SelectableLabel(self.getContainer(), text=text)
        lab.config(justify=CENTER, font=self._getContainerProperty('labelFont'), background=self._getContainerBg())
        lab.origBg = self._getContainerBg()
    lab.inContainer = False
    lab.DEFAULT_TEXT = text
    self.widgetManager.add(WIDGET_NAMES.Label, title, lab)
    self._positionWidget(lab, row, column, colspan, rowspan)
    return lab

def addLabelAutoEntry(

self, title, words, row=None, column=0, colspan=0, rowspan=0, secret=False, label=True)

def addLabelAutoEntry(self, title, words, row=None, column=0, colspan=0, rowspan=0, secret=False, label=True):
    return self._entryMaker(title, row, column, colspan, rowspan, secret=False, label=label, kind="auto", words=words)

def addLabelDirectoryEntry(

self, title, row=None, column=0, colspan=0, rowspan=0, label=True)

def addLabelDirectoryEntry(self, title, row=None, column=0, colspan=0, rowspan=0, label=True):
    return self._entryMaker(title, row, column, colspan, rowspan, secret=False, label=label, kind="directory")

def addLabelEntry(

self, title, row=None, column=0, colspan=0, rowspan=0, secret=False, label=True)

adds an entry box for capturing text, with the title as a label

def addLabelEntry(self, title, row=None, column=0, colspan=0, rowspan=0, secret=False, label=True):
    ''' adds an entry box for capturing text, with the title as a label '''
    return self._entryMaker(title, row, column, colspan, rowspan, secret, label=label)

def addLabelFileEntry(

self, title, row=None, column=0, colspan=0, rowspan=0, label=True)

adds an entry box with a button, that pops-up a file dialog, with a label that displays the title

def addLabelFileEntry(self, title, row=None, column=0, colspan=0, rowspan=0, label=True):
    ''' adds an entry box with a button, that pops-up a file dialog, with a label that displays the title '''
    return self._entryMaker(title, row, column, colspan, rowspan, secret=False, label=label, kind="file")

def addLabelNumericEntry(

self, title, row=None, column=0, colspan=0, rowspan=0, secret=False, label=True)

def addLabelNumericEntry(self, title, row=None, column=0, colspan=0, rowspan=0, secret=False, label=True):
    return self._entryMaker(title, row, column, colspan, rowspan, secret=secret, label=label, kind="numeric")

def addLabelOpenEntry(

self, title, row=None, column=0, colspan=0, rowspan=0, label=True)

adds an entry box with a button, that pops-up a open dialog, with a label that displays the title

def addLabelOpenEntry(self, title, row=None, column=0, colspan=0, rowspan=0, label=True):
    ''' adds an entry box with a button, that pops-up a open dialog, with a label that displays the title '''
    return self._entryMaker(title, row, column, colspan, rowspan, secret=False, label=label, kind="open")

def addLabelOptionBox(

self, title, options, row=None, column=0, colspan=0, rowspan=0, disabled='-', **kwargs)

Adds a new standard OptionBox, with a Label before it. Simply calls internal function _buildOptionBox, placing it in a LabelBox.

:param title: the key used to reference this OptionBox and text for the Label :param options: a list of values to put in the OptionBox, can be len 0 :returns: the created OptionBox (not the LabelBox) :raises ItemLookupError: if the title is already in use

def addLabelOptionBox(self, title, options, row=None, column=0, colspan=0, rowspan=0, disabled="-", **kwargs):
    """ Adds a new standard OptionBox, with a Label before it.
    Simply calls internal function _buildOptionBox, placing it in a LabelBox.
    :param title: the key used to reference this OptionBox and text for the Label
    :param options: a list of values to put in the OptionBox, can be len 0
    :returns: the created OptionBox (not the LabelBox)
    :raises ItemLookupError: if the title is already in use
    """
    frame = self._getLabelBox(title, **kwargs)
    option = self._buildOptionBox(frame, title, options, disabled=disabled)
    self._packLabelBox(frame, option)
    self._positionWidget(frame, row, column, colspan, rowspan)
    return option

def addLabelSaveEntry(

self, title, row=None, column=0, colspan=0, rowspan=0, label=True)

adds an entry box with a button, that pops-up a save dialog, with a label that displays the title

def addLabelSaveEntry(self, title, row=None, column=0, colspan=0, rowspan=0, label=True):
    ''' adds an entry box with a button, that pops-up a save dialog, with a label that displays the title '''
    return self._entryMaker(title, row, column, colspan, rowspan, secret=False, label=label, kind="save")

def addLabelScale(

self, title, row=None, column=0, colspan=0, rowspan=0, label=True)

adds a slidable scale, with a label showing the title at the specified position

def addLabelScale(self, title, row=None, column=0, colspan=0, rowspan=0, label=True):
    ''' adds a slidable scale, with a label showing the title  at the specified position '''
    frame = self._getLabelBox(title, label=label)
    scale = self._buildScale(title, frame)
    self._packLabelBox(frame, scale)
    self._positionWidget(frame, row, column, colspan, rowspan)
    return scale

def addLabelSecretEntry(

self, title, row=None, column=0, colspan=0, rowspan=0, label=True)

adds an entry box for capturing text, where the text is displayed as stars, with the title as a label

def addLabelSecretEntry(self, title, row=None, column=0, colspan=0, rowspan=0, label=True):
    ''' adds an entry box for capturing text, where the text is displayed as stars, with the title as a label '''
    return self._entryMaker(title, row, column, colspan, rowspan, secret=True, label=label)

def addLabelSpinBox(

self, title, values, row=None, column=0, colspan=0, rowspan=0, **kwargs)

adds a spinbox, with the specified values, and a label displaying the title

def addLabelSpinBox(self, title, values, row=None, column=0, colspan=0, rowspan=0, **kwargs):
    ''' adds a spinbox, with the specified values, and a label displaying the title '''
    frame = self._getLabelBox(title, **kwargs)
    spin = self._buildSpinBox(frame, title, values)
    self._packLabelBox(frame, spin)
    self._positionWidget(frame, row, column, colspan, rowspan)
    self.setSpinBoxPos(title, 0)
    return spin

def addLabelSpinBoxRange(

self, title, fromVal, toVal, row=None, column=0, colspan=0, rowspan=0, label=True, **kwargs)

adds a spinbox, with a range of whole numbers, and a label displaying the title

def addLabelSpinBoxRange(self, title, fromVal, toVal, row=None, column=0, colspan=0, rowspan=0, label=True, **kwargs):
    ''' adds a spinbox, with a range of whole numbers, and a label displaying the title '''
    vals = list(range(fromVal, toVal + 1))
    spin = self.addLabelSpinBox(title, vals, row, column, colspan, rowspan, label=label)
    spin.isRange = True
    return spin

def addLabelTickOptionBox(

self, title, options, row=None, column=0, colspan=0, rowspan=0, disabled='-', **kwargs)

Adds a new TickOptionBox, with a Label before it Simply calls internal function _buildOptionBox, placing it in a LabelBox

:param title: the key used to reference this TickOptionBox, and text for the Label :param options: a list of values to put in the TickOptionBox, can be len 0 :returns: the created TickOptionBox (not the LabelBox) :raises ItemLookupError: if the title is already in use

def addLabelTickOptionBox(self, title, options, row=None, column=0, colspan=0, rowspan=0, disabled="-", **kwargs):
    """ Adds a new TickOptionBox, with a Label before it
    Simply calls internal function _buildOptionBox, placing it in a LabelBox
    :param title: the key used to reference this TickOptionBox, and text for the Label
    :param options: a list of values to put in the TickOptionBox, can be len 0
    :returns: the created TickOptionBox (not the LabelBox)
    :raises ItemLookupError: if the title is already in use
    """
    frame = self._getLabelBox(title, **kwargs)
    tick = self._buildOptionBox(frame, title, options, kind="ticks", disabled=disabled)
    self._packLabelBox(frame, tick)
    self._positionWidget(frame, row, column, colspan, rowspan)
    return tick

def addLabelValidationEntry(

self, title, row=None, column=0, colspan=0, rowspan=0, secret=False, label=True)

def addLabelValidationEntry(self, title, row=None, column=0, colspan=0, rowspan=0, secret=False, label=True):
    return self._entryMaker(title, row, column, colspan, rowspan, secret=False, label=label, kind="validation")

def addLabels(

self, names, row=None, colspan=0, rowspan=0)

adds a set of labels, in the row, spannning specified columns

def addLabels(self, names, row=None, colspan=0, rowspan=0):
    ''' adds a set of labels, in the row, spannning specified columns '''
    frame = self._makeWidgetBox()(self.getContainer())
    if not self.ttkFlag:
        frame.config(background=self._getContainerBg())
    for i in range(len(names)):
        self.widgetManager.verify(WIDGET_NAMES.Label, names[i])
        if not self.ttkFlag:
            lab = Label(frame, text=names[i])
            lab.config(font=self._getContainerProperty('labelFont'), justify=LEFT, background=self._getContainerBg())
        else:
            lab = ttk.Label(frame, text=names[i])
        lab.DEFAULT_TEXT = names[i]
        lab.inContainer = False
        self.widgetManager.add(WIDGET_NAMES.Label, names[i], lab)
        lab.grid(row=0, column=i)
        Grid.columnconfigure(frame, i, weight=1)
        Grid.rowconfigure(frame, 0, weight=1)
        frame.theWidgets.append(lab)
    self._positionWidget(frame, row, 0, colspan, rowspan)
    self.widgetManager.log(WIDGET_NAMES.FrameBox, frame)

adds a hyperlink to the specified function

def addListBox(

self, name, values=None, row=None, column=0, colspan=0, rowspan=0)

adds a list box, with the the specified list of values

def addListBox(self, name, values=None, row=None, column=0, colspan=0, rowspan=0):
    ''' adds a list box, with the the specified list of values '''
    self.widgetManager.verify(WIDGET_NAMES.ListBox, name)
    container = self.makeListBoxContainer()(self.getContainer())
    vscrollbar = AutoScrollbar(container)
    hscrollbar = AutoScrollbar(container, orient=HORIZONTAL)
    container.lb = Listbox(container,
        yscrollcommand=vscrollbar.set,
        xscrollcommand=hscrollbar.set)
    vscrollbar.grid(row=0, column=1, sticky=N + S)
    hscrollbar.grid(row=1, column=0, sticky=E + W)
    container.lb.grid(row=0, column=0, sticky=N + S + E + W)
    container.grid_rowconfigure(0, weight=1)
    container.grid_columnconfigure(0, weight=1)
    vscrollbar.config(command=container.lb.yview)
    hscrollbar.config(command=container.lb.xview)
    container.lb.config(font=self._getContainerProperty('inputFont'))
    self.widgetManager.add(WIDGET_NAMES.ListBox, name, container.lb)
    container.lb.DEFAULT_TEXT=""
    if values is not None:
        container.lb.DEFAULT_TEXT='\n'.join(str(x) for x in values)
        for name in values:
            container.lb.insert(END, name)
    self._positionWidget(container, row, column, colspan, rowspan)
    return container.lb

def addListItem(

self, title, item, pos=None, select=True)

add the item to the end of the specified list box

def addListItem(self, title, item, pos=None, select=True):
    ''' add the item to the end of the specified list box '''
    lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title)
    # add it at the end
    if pos is None: pos = END
    lb.insert(pos, item)
    # show & select the newly added item
    if select:
        # clear any selection
        items = lb.curselection()
        if len(items) > 0:
            lb.selection_clear(items)
        self.selectListItemAtPos(title, lb.size() - 1)

def addListItems(

self, title, items, select=True)

adds the list of items to the specified list box

def addListItems(self, title, items, select=True):
    ''' adds the list of items to the specified list box '''
    for i in items:
        self.addListItem(title, i, select=select)

def addMenu(

self, name, func, shortcut=None, underline=-1)

def addMenu(self, name, func, shortcut=None, underline=-1):
    self.addMenuItem(None, name, func=func, kind="topLevel", shortcut=shortcut, underline=underline)

def addMenuCheckBox(

self, menu, name, func=None, shortcut=None, underline=-1)

def addMenuCheckBox(self, menu, name, func=None, shortcut=None, underline=-1):
    self.addMenuItem(menu, name, func, "cb", shortcut, underline)

def addMenuEdit(

self, inMenuBar=False)

def addMenuEdit(self, inMenuBar=False):
    self._initMenu()
    self.copyAndPaste.inUse = True
    # in case we already made the menu - just return
    try: self.widgetManager.verify(WIDGET_NAMES.Menu, "EDIT")
    except: return
    editMenu = Menu(self.menuBar, tearoff=False)
    editMenu.bind("<FocusOut>", lambda e: editMenu.unpost())
    if inMenuBar:
        self.menuBar.add_cascade(menu=editMenu, label='Edit ')
    self.widgetManager.add(WIDGET_NAMES.Menu, "EDIT", editMenu)
    if gui.GET_PLATFORM() == gui.MAC:
        shortcut = "Command-"
    else:
        shortcut = "Control-"
    eList = [
        ('Cut', lambda e: self._copyAndPasteHelper("Cut"), "X", False),
        ('Copy', lambda e: self._copyAndPasteHelper("Copy"), "C", False),
        ('Paste', lambda e: self._copyAndPasteHelper("Paste"), "V", False),
        ('Select All', lambda e: self._copyAndPasteHelper("Select All"), "A", True if gui.GET_PLATFORM() == gui.MAC else False),
        ('Clear Clipboard', lambda e: self._copyAndPasteHelper("Clear Clipboard"), None, False)
        ]
    for (txt, cmd, sc, bind) in eList:
        acc = None if sc is None else shortcut + sc
        self.addMenuItem("EDIT", txt, cmd, shortcut=acc, createBinding=bind)
    # add a clear option
    self.addMenuSeparator("EDIT")
    self.addMenuItem("EDIT", "Clear All", lambda e: self._copyAndPasteHelper("Clear All"))
    self.addMenuSeparator("EDIT")
    self.addMenuItem("EDIT", 'Undo', lambda e: self._copyAndPasteHelper("Undo"), shortcut=shortcut + "Z", createBinding=False)
    self.addMenuItem("EDIT", 'Redo', lambda e: self._copyAndPasteHelper( "Redo"), shortcut=shortcut+"Shift-Z", createBinding=True)
    self.addMenuSeparator("EDIT")
    self.addMenuItem("EDIT", "Bold", lambda e: self._copyAndPasteHelper("BOLD"), shortcut=shortcut+"B")
    self.addMenuItem("EDIT", "Italic", lambda e: self._copyAndPasteHelper("ITALIC"), shortcut=shortcut+"I")
    self.addMenuItem("EDIT", "Underline", lambda e: self._copyAndPasteHelper("UNDERLINE"), shortcut=shortcut+"U")
    self.addMenuItem("EDIT", "Bold & Italic", lambda e: self._copyAndPasteHelper("BOLD_ITALIC"), shortcut=shortcut+"Shift-B")
    self.disableMenu("EDIT")

def addMenuHelp(

self, func)

def addMenuHelp(self, func):
    if self.platform == self.MAC:
        self._initMenu()
        helpMenu = Menu(self.menuBar, name='help')
        self.menuBar.add_cascade(menu=helpMenu, label='Help')
        u = self.MAKE_FUNC(func, "help")
        self.topLevel.createcommand('tk::mac::ShowHelp', u)
        self.widgetManager.add(WIDGET_NAMES.Menu, "MAC_HELP", helpMenu)
    else:
        self.warn("The Help Menu is specific to Mac OSX")

def addMenuItem(

self, title, item, func=None, kind=None, shortcut=None, underline=-1, rb_id=None, createBinding=True)

def addMenuItem(self, title, item, func=None, kind=None, shortcut=None, underline=-1, rb_id=None, createBinding=True):
    # set the initial menubar
    self._initMenu()
    # get or create an initial menu
    if title is not None:
        try:
            theMenu = self.widgetManager.get(WIDGET_NAMES.Menu, title)
        except:
            theMenu = self.createMenu(title)
            if theMenu is None:
                gui.warn('Unable to create menu: %s', title)
                return
    if underline > -1 and self.platform == self.MAC:
        gui.warn("Underlining menu items not available on MAC")
    if func is not None:
        func = self.MAKE_FUNC(func, item)
    acc = None
    if shortcut is not None:
        if kind == 'cb':
            f = lambda e: self._menuCheckButtonBind(title, item, func)
            binding = EventBinding(shortcut, f, self._getTopLevel(), menuBinding=True)
        else:
            binding = EventBinding(shortcut, func, self._getTopLevel(), menuBinding=True)
        try:
            self.widgetManager.add(WIDGET_NAMES.Bindings, binding.displayName, binding)
            if createBinding: binding.createBindings()
            acc = binding.displayName
        except ItemLookupError:
            raise ItemLookupError('Unable to bind menu ' + item + ' to ' + binding.displayName + ' - binding already exists')
    # now, let's create the actual menu item
    if item == "-" or kind == "separator":
        theMenu.add_separator()
    elif kind == "topLevel" or title is None:
        if self.platform == self.MAC:
            self.warn("Unable to make topLevel menus (%s) on Mac", item)
        else:
            self.menuBar.add_command(
                label=item, command=func, accelerator=acc, underline=underline)
    elif kind == "rb":
        varName = title + "rb" + item
        newRb = False
        if (varName in self.widgetManager.group(WIDGET_NAMES.Menu, group=WidgetManager.VARS)):
            var = self.widgetManager.get(WIDGET_NAMES.Menu, varName, group=WidgetManager.VARS)
        else:
            newRb = True
            var = StringVar(self.topLevel)
            self.widgetManager.add(WIDGET_NAMES.Menu, varName, var, group=WidgetManager.VARS)
        theMenu.add_radiobutton(label=rb_id, command=func, variable=var, value=rb_id, accelerator=acc, underline=underline)
        if newRb:
            self.setMenuRadioButton(title, item, rb_id)
    elif kind == "cb":
        varName = title + "cb" + item
        self.widgetManager.verify(WIDGET_NAMES.Menu, varName, group=WidgetManager.VARS)
        var = BooleanVar(self.topLevel)
        var.set(False)
        self.widgetManager.add(WIDGET_NAMES.Menu, varName, var, group=WidgetManager.VARS)
        theMenu.add_checkbutton(label=item, command=func, variable=var, onvalue=True, offvalue=False, accelerator=acc, underline=underline)
    elif kind == "sub":
        self.widgetManager.verify(WIDGET_NAMES.Menu, item)
        subMenu = Menu(theMenu, tearoff=False)
        self.widgetManager.add(WIDGET_NAMES.Menu, item, subMenu)
        theMenu.add_cascade(label=item, menu=subMenu)
    else:
        theMenu.add_command(label=item, command=func, accelerator=acc, underline=underline)

def addMenuList(

self, menuName, names, funcs)

def addMenuList(self, menuName, names, funcs):
    # deal with a dict_keys object - messy!!!!
    if not isinstance(names, list):
        names = list(names)
    # append some Nones, if it's a list and contains separators
    if funcs is not None:
        if not callable(funcs):
            seps = names.count("-")
            for i in range(seps):
                funcs.append(None)
        singleFunc = self._checkFunc(names, funcs)
    # add menu items
    for t in names:
        if funcs is None:
            u = None
        elif singleFunc is not None:
            u = singleFunc
        else:
            u = funcs.pop(0)
        self.addMenuItem(menuName, t, u)

def addMenuPreferences(

self, func)

def addMenuPreferences(self, func):
    if self.platform == self.MAC:
        self._initMenu()
        u = self.MAKE_FUNC(func, "preferences")
        self.topLevel.createcommand('tk::mac::ShowPreferences', u)
    else:
        self.warn("The Preferences Menu is specific to Mac OSX")

def addMenuRadioButton(

self, menu, name, value, func=None, shortcut=None, underline=-1)

def addMenuRadioButton(self, menu, name, value, func=None, shortcut=None, underline=-1):
    self.addMenuItem(menu, name, func, "rb", shortcut, underline, value)

def addMenuSeparator(

self, menu)

def addMenuSeparator(self, menu):
    self.addMenuItem(menu, "-")

def addMenuWindow(

self)

def addMenuWindow(self):
    if self.platform == self.MAC:
        self._initMenu()
        windowMenu = Menu(self.menuBar, name='window')
        self.menuBar.add_cascade(menu=windowMenu, label='Window')
        self.widgetManager.add(WIDGET_NAMES.Menu, "MAC_WIN", windowMenu)
    else:
        self.warn("The Window Menu is specific to Mac OSX")

def addMessage(

self, title, text=None, row=None, column=0, colspan=0, rowspan=0)

adds a message box, to display text across multiple lines

def addMessage(self, title, text=None, row=None, column=0, colspan=0, rowspan=0):
    ''' adds a message box, to display text across multiple lines '''
    self.widgetManager.verify(WIDGET_NAMES.Message, title)
    if text is None:
        text = title
        gui.trace("Not specifying text for messages (%s) now uses the title for the text. If you want an empty message, pass an empty string ''", title)
    mess = Message(self.getContainer())
    mess.config(text=text)
    mess.config(font=self._getContainerProperty('labelFont'))
    mess.config(justify=LEFT, background=self._getContainerBg())
    mess.DEFAULT_TEXT = text
    if self.platform in [self.MAC, self.LINUX]:
        mess.config(highlightbackground=self._getContainerBg())
    self.widgetManager.add(WIDGET_NAMES.Message, title, mess)
    self._positionWidget(mess, row, column, colspan, rowspan)
         mess.bind("<Configure>", lambda e: mess.config(width=e.width-10))
    return mess

def addMeter(

self, name, row=None, column=0, colspan=0, rowspan=0)

def addMeter(self, name, row=None, column=0, colspan=0, rowspan=0):
    return self._addMeter(name, "METER", row, column, colspan, rowspan)

def addMicroBit(

self, title, row=None, column=0, colspan=0, rowspan=0)

adds a simple microbit widget used with permission from Ben Goodwin

def addMicroBit(self, title, row=None, column=0, colspan=0, rowspan=0):
    ''' adds a simple microbit widget
         used with permission from Ben Goodwin '''
    self.widgetManager.verify(WIDGET_NAMES.MicroBit, title)
    mb = MicroBitSimulator(self.getContainer())
    self._positionWidget(mb, row, column, colspan, rowspan)
    self.widgetManager.add(WIDGET_NAMES.MicroBit, title, mb)
    return mb

def addNamedButton(

self, name, title, func, row=None, column=0, colspan=0, rowspan=0)

adds a button, displaying the name as its text

def addNamedButton(self, name, title, func, row=None, column=0, colspan=0, rowspan=0):
    ''' adds a button, displaying the name as its text '''
    but = self._buildButton(title, func, self.getContainer(), name)
    self._positionWidget(but, row, column, colspan, rowspan, None)
    return but

def addNamedCheckBox(

self, name, title, row=None, column=0, colspan=0, rowspan=0)

adds a new check box, at the specified position, with the name as the text

def addNamedCheckBox(self, name, title, row=None, column=0, colspan=0, rowspan=0):
    ''' adds a new check box, at the specified position, with the name as the text '''
    return self.addCheckBox(title, row, column, colspan, rowspan, name)

def addNumericEntry(

self, title, row=None, column=0, colspan=0, rowspan=0, secret=False)

def addNumericEntry(self, title, row=None, column=0, colspan=0, rowspan=0, secret=False):
    return self._entryMaker(title, row, column, colspan, rowspan, secret=secret, label=False, kind="numeric")

def addNumericLabelEntry(

self, title, row=None, column=0, colspan=0, rowspan=0, secret=False, label=True)

def addNumericLabelEntry(self, title, row=None, column=0, colspan=0, rowspan=0, secret=False, label=True):
    return self._entryMaker(title, row, column, colspan, rowspan, secret=secret, label=label, kind="numeric")

def addOpenEntry(

self, title, row=None, column=0, colspan=0, rowspan=0)

adds an entry box with a button, that pops-up a open dialog

def addOpenEntry(self, title, row=None, column=0, colspan=0, rowspan=0):
    ''' adds an entry box with a button, that pops-up a open dialog '''
    return self._entryMaker(title, row, column, colspan, rowspan, secret=False, label=False, kind="open")

def addOptionBox(

self, title, options, row=None, column=0, colspan=0, rowspan=0, disabled='-', **kwargs)

Adds a new standard OptionBox. Simply calls internal function _buildOptionBox.

:param title: the key used to reference this OptionBox :param options: a list of values to put in the OptionBox, can be len 0 :returns: the created OptionBox :raises ItemLookupError: if the title is already in use

def addOptionBox(self, title, options, row=None, column=0, colspan=0, rowspan=0, disabled='-', **kwargs):
    """ Adds a new standard OptionBox.
    Simply calls internal function _buildOptionBox.
    :param title: the key used to reference this OptionBox
    :param options: a list of values to put in the OptionBox, can be len 0
    :returns: the created OptionBox
    :raises ItemLookupError: if the title is already in use
    """
    option = self._buildOptionBox(self.getContainer(), title, options, disabled=disabled)
    self._positionWidget(option, row, column, colspan, rowspan)
    return option

def addPieChart(

self, name, fracs, row=None, column=0, colspan=0, rowspan=0)

def addPieChart(self, name, fracs, row=None, column=0, colspan=0, rowspan=0):
    self.widgetManager.verify(WIDGET_NAMES.PieChart, name)
    self._loadTooltip()
    pie = PieChart(self.getContainer(), fracs, self._getContainerBg())
    self.widgetManager.add(WIDGET_NAMES.PieChart, name, pie)
    self._positionWidget(pie, row, column, colspan, rowspan, sticky=None)
    return pie

def addPlot(

self, title, t, s, row=None, column=0, colspan=0, rowspan=0, width=None, height=None, showNav=False)

adds a MatPlotLib, with t/s plotted

def addPlot(self, title, t, s, row=None, column=0, colspan=0, rowspan=0, width=None, height=None, showNav=False):
    ''' adds a MatPlotLib, with t/s plotted '''
    canvas, fig = self._addPlotFig(title, row, column, colspan, rowspan, width, height, showNav)
    axes = fig.add_subplot(111)
    axes.plot(t,s)
    canvas.axes = axes
    return axes

def addPlotFig(

self, title, row=None, column=0, colspan=0, rowspan=0, width=None, height=None, showNav=False)

def addPlotFig(self, title, row=None, column=0, colspan=0, rowspan=0, width=None, height=None, showNav=False):
    canvas, fig = self._addPlotFig(title, row, column, colspan, rowspan, width, height, showNav)
    return fig

def addProperties(

self, title, values=None, row=None, column=0, colspan=0, rowspan=0, **kwargs)

adds a new properties widget, displaying the dictionary of booleans as tick boxes

def addProperties(self, title, values=None, row=None, column=0, colspan=0, rowspan=0, **kwargs):
    ''' adds a new properties widget, displaying the dictionary of booleans as tick boxes '''
    self.widgetManager.verify(WIDGET_NAMES.Properties, title)
    haveTitle = True
    if self._getContainerProperty('type') == WIDGET_NAMES.ToggleFrame:
        self.containerStack[-1]['sticky'] = "ew"
        haveTitle = False
    props = Properties(self.getContainer(), title, values, haveTitle,
                        font=self._getContainerProperty('labelFont'), background=self._getContainerBg())
    self._positionWidget(props, row, column, colspan, rowspan)
    self.widgetManager.add(WIDGET_NAMES.Properties, title, props)
    return props

def addRadioButton(

self, title, name, row=None, column=0, colspan=0, rowspan=0)

adds a radio button, to thr group 'title' with the text 'name'

def addRadioButton(self, title, name, row=None, column=0, colspan=0, rowspan=0):
    ''' adds a radio button, to thr group 'title' with the text 'name' '''
    ident = title + "-" + name
    self.widgetManager.verify(WIDGET_NAMES.RadioButton, ident)
    var = None
    newRb = False
    # title - is the grouper
    # so, if we already have an entry in n_rbVars - get it
    if (title in self.widgetManager.group(WIDGET_NAMES.RadioButton, group=WidgetManager.VARS)):
        var = self.widgetManager.get(WIDGET_NAMES.RadioButton, title, group=WidgetManager.VARS)
    else:
        # if this is a new grouper - set it all up
        var = StringVar(self.topLevel)
        self.widgetManager.add(WIDGET_NAMES.RadioButton, title, var, group=WidgetManager.VARS)
        newRb = True
    # finally, create the actual RadioButton
    if not self.ttkFlag:
        rb = Radiobutton(self.getContainer(), text=name, variable=var, value=name)
        rb.config(anchor=W, background=self._getContainerBg(), indicatoron=1,
            activebackground=self._getContainerBg(), font=self._getContainerProperty('labelFont')
        )
    else:
        rb = ttk.Radiobutton(self.getContainer(), text=name, variable=var, value=name)
    rb.bind("<Button-1>", self._grabFocus)
    rb.DEFAULT_TEXT = name
    self.widgetManager.add(WIDGET_NAMES.RadioButton, ident, rb)
    #rb.bind("<Tab>", self._focusNextWindow)
    #rb.bind("<Shift-Tab>", self._focusLastWindow)
    # and select it, if it's the first item in the list
    if newRb:
        rb.select() if not self.ttkFlag else rb.invoke()
        var.startVal = name # so we can reset it...
    self._positionWidget(rb, row, column, colspan, rowspan, EW)
    return rb

def addSaveEntry(

self, title, row=None, column=0, colspan=0, rowspan=0)

adds an entry box with a button, that pops-up a save dialog

def addSaveEntry(self, title, row=None, column=0, colspan=0, rowspan=0):
    ''' adds an entry box with a button, that pops-up a save dialog '''
    return self._entryMaker(title, row, column, colspan, rowspan, secret=False, label=False, kind="save")

def addScale(

self, title, row=None, column=0, colspan=0, rowspan=0)

adds a slidable scale at the specified position

def addScale(self, title, row=None, column=0, colspan=0, rowspan=0):
    ''' adds a slidable scale at the specified position '''
    scale = self._buildScale(title, self.getContainer())
    self._positionWidget(scale, row, column, colspan, rowspan)
    return scale

def addScrolledTextArea(

self, title, row=None, column=0, colspan=0, rowspan=0, text=None)

Adds a Scrollable TextArea with the specified title Simply calls internal _buildTextArea functio, specifying a ScrollabelTextArea before positioning the widget

:param title: the key used to reference this TextArea :returns: the created TextArea :raises ItemLookupError: if the title is already in use

def addScrolledTextArea(self, title, row=None, column=0, colspan=0, rowspan=0, text=None):
    """ Adds a Scrollable TextArea with the specified title
    Simply calls internal _buildTextArea functio, specifying a ScrollabelTextArea before positioning the widget
    :param title: the key used to reference this TextArea
    :returns: the created TextArea
    :raises ItemLookupError: if the title is already in use
    """
    txt = self._buildTextArea(title, self.getContainer(), True)
    self._positionWidget(txt, row, column, colspan, rowspan, N+E+S+W)
    if text is not None: self.setTextArea(title, text, callFunction=False)
    return txt

def addSecretEntry(

self, title, row=None, column=0, colspan=0, rowspan=0)

adds an entry box for capturing text, where the text is displayed as stars

def addSecretEntry(self, title, row=None, column=0, colspan=0, rowspan=0):
    ''' adds an entry box for capturing text, where the text is displayed as stars '''
    return self._entryMaker(title, row, column, colspan, rowspan, True)

def addSecretLabelEntry(

self, title, row=None, column=0, colspan=0, rowspan=0, label=True)

adds an entry box for capturing text, where the text is displayed as stars, with the title as a label

def addSecretLabelEntry(self, title, row=None, column=0, colspan=0, rowspan=0, label=True):
    ''' adds an entry box for capturing text, where the text is displayed as stars, with the title as a label '''
    return self._entryMaker(title, row, column, colspan, rowspan, secret=True, label=label)

def addSelectableLabel(

self, title, text=None, row=None, column=0, colspan=0, rowspan=0)

adds a label with selectable text

def addSelectableLabel(self, title, text=None, row=None, column=0, colspan=0, rowspan=0):
    ''' adds a label with selectable text '''
    return self.addLabel(title, text, row, column, colspan, rowspan, selectable=True)

def addSpinBox(

self, title, values, row=None, column=0, colspan=0, rowspan=0, **kwargs)

adds a spinbox, with the specified values

def addSpinBox(self, title, values, row=None, column=0, colspan=0, rowspan=0, **kwargs):
    ''' adds a spinbox, with the specified values '''
    return self._addSpinBox(title, values, row, column, colspan, rowspan)

def addSpinBoxRange(

self, title, fromVal, toVal, row=None, column=0, colspan=0, rowspan=0, **kwargs)

adds a spinbox, with a range of whole numbers

def addSpinBoxRange(self, title, fromVal, toVal, row=None, column=0, colspan=0, rowspan=0, **kwargs):
    ''' adds a spinbox, with a range of whole numbers '''
    vals = list(range(fromVal, toVal + 1))
    spin = self._addSpinBox(title, vals, row, column, colspan, rowspan)
    spin.isRange = True
    return spin

def addSplitMeter(

self, name, row=None, column=0, colspan=0, rowspan=0)

def addSplitMeter(self, name, row=None, column=0, colspan=0, rowspan=0):
    return self._addMeter(name, "SPLIT", row, column, colspan, rowspan)

def addStatusbar(

self, header='', fields=1, side=None)

def addStatusbar(self, header="", fields=1, side=None):
    if not self.hasStatus:
        class Statusbar(Frame, object):
            def __init__(self, master, **kwargs):
                super(Statusbar, self).__init__(master, **kwargs)
        self.hasStatus = True
        self.header = header
        self.statusFrame = Statusbar(self.appWindow)
        self.statusFrame.config(bd=1, relief=SUNKEN)
        self.statusFrame.pack(side=BOTTOM, fill=X, anchor=S)
        self._statusFields = []
        for i in range(fields):
            self._statusFields.append(Label(self.statusFrame))
            self._statusFields[i].config(
                bd=1,
                relief=SUNKEN,
                anchor=W,
                font=self._statusFont,
                width=10)
            self._addTooltip(self._statusFields[i], "Status bar", True)
            if side == "LEFT":
                self._statusFields[i].pack(side=LEFT)
            elif side == "RIGHT":
                self._statusFields[i].pack(side=RIGHT)
            else:
                self._statusFields[i].pack(side=LEFT, expand=1, fill=BOTH)
    else:
        self.error("Statusbar already exists - ignoring")

def addSubMenu(

self, menu, subMenu)

def addSubMenu(self, menu, subMenu):
    self.addMenuItem(menu, subMenu, func=None, kind="sub")

def addTable(

self, title, data, row=None, column=0, colspan=0, rowspan=0, action=None, addRow=None, actionHeading='Action', actionButton='Press', addButton='Add', showMenu=False, border='solid', **kwargs)

creates a new table, displaying the specified data

def addTable(self, title, data, row=None, column=0, colspan=0, rowspan=0, action=None, addRow=None,
            actionHeading="Action", actionButton="Press", addButton="Add", showMenu=False, border="solid", **kwargs):
    ''' creates a new table, displaying the specified data '''
    self.widgetManager.verify(WIDGET_NAMES.Table, title)
    wrap=kwargs.pop('wrap', 250)
    horiz=kwargs.pop('horizontal', True)
    if not self.ttkFlag:
        grid = SimpleTable(self.getContainer(), title, data,
                    action, addRow,
                    actionHeading, actionButton, addButton,
                    showMenu, buttonFont=self._getContainerProperty('buttonFont'),
                    font=self.tableFont, background=self._getContainerBg(),
                    queueFunction=self.queueFunction, border=border, wrap=wrap, horizontal=horiz
                )
    else:
        grid = SimpleTable(self.getContainer(), title, data,
                    action, addRow,
                    actionHeading, actionButton, addButton,
                    showMenu, buttonFont=self._getContainerProperty('buttonFont'),
                    queueFunction=self.queueFunction, border=border, wrap=wrap, horizontal=horiz
                )
    self._positionWidget(grid, row, column, colspan, rowspan, N+E+S+W)
    self.widgetManager.add(WIDGET_NAMES.Table, title, grid)
    return grid

def addTableColumn(

self, title, columnNumber, data)

adds a new column of data, in the specified position, to the specified table

def addTableColumn(self, title, columnNumber, data):
    ''' adds a new column of data, in the specified position, to the specified table '''
    grid = self.widgetManager.get(WIDGET_NAMES.Table, title)
    grid.addColumn(columnNumber, data)

def addTableRow(

self, title, data)

adds a new row of data to the specified table

def addTableRow(self, title, data):
    ''' adds a new row of data to the specified table '''
    grid = self.widgetManager.get(WIDGET_NAMES.Table, title)
    grid.addRow(data)

def addTableRows(

self, title, data)

adds multiple rows of data to the specified table

def addTableRows(self, title, data):
    ''' adds multiple rows of data to the specified table '''
    grid = self.widgetManager.get(WIDGET_NAMES.Table, title)
    grid.addRows(data, scroll=True)

def addTextArea(

self, title, row=None, column=0, colspan=0, rowspan=0, text=None)

Adds a TextArea with the specified title Simply calls internal _buildTextArea function before positioning the widget

:param title: the key used to reference this TextArea :returns: the created TextArea :raises ItemLookupError: if the title is already in use

def addTextArea(self, title, row=None, column=0, colspan=0, rowspan=0, text=None):
    """ Adds a TextArea with the specified title
    Simply calls internal _buildTextArea function before positioning the widget
    :param title: the key used to reference this TextArea
    :returns: the created TextArea
    :raises ItemLookupError: if the title is already in use
    """
    txt = self._buildTextArea(title, self.getContainer())
    self._positionWidget(txt, row, column, colspan, rowspan, N+E+S+W)
    if text is not None: self.setTextArea(title, text, callFunction=False)
    return txt

def addTickOptionBox(

self, title, options, row=None, column=0, colspan=0, rowspan=0, disabled='-', **kwargs)

Adds a new TickOptionBox. Simply calls internal function _buildOptionBox.

:param title: the key used to reference this TickOptionBox :param options: a list of values to put in the TickOptionBox, can be len 0 :returns: the created TickOptionBox :raises ItemLookupError: if the title is already in use

def addTickOptionBox(self, title, options, row=None, column=0, colspan=0, rowspan=0, disabled="-", **kwargs):
    """ Adds a new TickOptionBox.
    Simply calls internal function _buildOptionBox.
    :param title: the key used to reference this TickOptionBox
    :param options: a list of values to put in the TickOptionBox, can be len 0
    :returns: the created TickOptionBox
    :raises ItemLookupError: if the title is already in use
    """
    tick = self._buildOptionBox(self.getContainer(), title, options, kind="ticks", disabled=disabled)
    self._positionWidget(tick, row, column, colspan, rowspan)
    return tick

def addToolbar(

self, names, funcs, findIcon=False, **kwargs)

def addToolbar(self, names, funcs, findIcon=False, **kwargs):
    # hide the toolbarMin bar
    if self.tb.toolbarMin is not None:
        self.tb.toolbarMin.pack_forget()
    # make sure the toolbar is showing
    try:
        self.tb.pack_info()
    except:
        self.tb.location = self.containerStack[0]['container']
        self.tb.pack(before=self.tb.location, side=TOP, fill=X)
    if not self.tb.inUse:
        self.tb.inUse = True
    image = None
    singleFunc = self._checkFunc(names, funcs)
    if not isinstance(names, list):
        names = [names]
    for i in range(len(names)):
        t = names[i]
        if (t in self.widgetManager.group(WIDGET_NAMES.Toolbar)):
            raise Exception(
                "Invalid toolbar button name: " +
                t +
                " already exists")
        if findIcon:
            # turn off warnings about PNGs
            with PauseLogger():
                imgFile = os.path.join(self.icon_path, t.lower() + ".png")
                try:
                    image = self._getImage(imgFile)
                except Exception as e:
                    image = None
        if not self.ttkFlag:
            but = Button(self.tb)
            but.config(relief=FLAT, font=self._buttonFont)
            if gui.GET_PLATFORM() == gui.MAC and self.tb.BG_COLOR is not None:
                but.config(highlightbackground=self.tb.BG_COLOR)
        else:
            but = ttk.Button(self.tb)
        self.widgetManager.add(WIDGET_NAMES.Toolbar, t, but)
        if singleFunc is not None:
            u = self.MAKE_FUNC(singleFunc, t)
        else:
            u = self.MAKE_FUNC(funcs[i], t)
        but.config(command=u)
        if image is not None:
            # works on Mac & Windows :)
            but.config(image=image)
            but.image = image
            if not self.ttkFlag:
                but.config(justify=LEFT, compound=TOP)
            else:
                but.config(style="Toolbar.TButton")
        else:
            but.config(text=t)
        but.pack(side=LEFT, padx=2, pady=2)
        but.tt_var = self._addTooltip(but, t.title(), True)
        but.DEFAULT_TEXT=t

def addToolbarButton(

self, name, func, findIcon=False)

def addToolbarButton(self, name, func, findIcon=False):
    self.addToolbar([name], func, findIcon)

def addTrashBin(

self, title, row=None, column=0, colspan=0, rowspan=0)

NOT IN USE - adds a trashbin, for discarding dragged items

def addTrashBin(self, title, row=None, column=0, colspan=0, rowspan=0):
    ''' NOT IN USE - adds a trashbin, for discarding dragged items '''
    trash = TrashBin(self.getContainer())
    self._positionWidget(trash, row, column, colspan, rowspan)
    return trash

def addTree(

self, title, data, row=None, column=0, colspan=0, rowspan=0)

adds a navigatable tree, displaying the specified xml text

def addTree(self, title, data, row=None, column=0, colspan=0, rowspan=0):
    ''' adds a navigatable tree, displaying the specified xml text '''
    self.widgetManager.verify(WIDGET_NAMES.Tree, title)
    self._importAjtree()
    if parseString is False:
        self.warn("Unable to parse xml files. .addTree() not available")
        return
    if isinstance(data, UNIVERSAL_STRING):
        data = parseString(data)
    else:
        pass # assume xml object
        
    return self._buildTree(title, data, row, column, colspan, rowspan)

def addTreeChild(

self, title, data)

def addTreeChild(self, title, data):
    tree = self.widgetManager.get(WIDGET_NAMES.Tree, title)
    if isinstance(data, UNIVERSAL_STRING):
        data = parseString(data)
    treeData = self._makeAjTreeData()(data)
    tree.addChild(treeData)

def addTurtle(

self, title, row=None, column=0, colspan=0, rowspan=0)

adds a turtle widget at the specified position

def addTurtle(self, title, row=None, column=0, colspan=0, rowspan=0):
    ''' adds a turtle widget at the specified position '''
    self._loadTurtle()
    if turtle is False:
        raise Exception("Unable to load turtle")
    self.widgetManager.verify(WIDGET_NAMES.Turtle, title)
    canvas = Canvas(self.getContainer())
    canvas.screen = turtle.TurtleScreen(canvas)
    self._positionWidget(canvas, row, column, colspan, rowspan)
    self.widgetManager.add(WIDGET_NAMES.Turtle, title, canvas)
    canvas.turtle = turtle.RawTurtle(canvas.screen)
    return canvas.turtle

def addValidationEntry(

self, title, row=None, column=0, colspan=0, rowspan=0, secret=False)

def addValidationEntry(self, title, row=None, column=0, colspan=0, rowspan=0, secret=False):
    return self._entryMaker(title, row, column, colspan, rowspan, secret=False, label=False, kind="validation")

def addVerticalSeparator(

self, row=None, column=0, colspan=0, rowspan=0, colour=None)

def addVerticalSeparator(self, row=None, column=0, colspan=0, rowspan=0, colour=None):
    return self._addSeparator("vertical", row, column, colspan, rowspan, colour)

adds a hyperlink to the specified web page

def addWidget(

self, title, widg, row=None, column=0, colspan=0, rowspan=0)

adds a generic widget to the appJar grid manager

def addWidget(self, title, widg, row=None, column=0, colspan=0, rowspan=0):
    ''' adds a generic widget to the appJar grid manager '''
    self.widgetManager.verify(WIDGET_NAMES.Widget, title)
    self._positionWidget(widg, row, column, colspan, rowspan)
    self.widgetManager.add(WIDGET_NAMES.Widget, title, widg)

def after(

self, delay_ms, callback=None, *args)

wrapper for topLevel after function schedules the callback function to happen in x seconds returns an ID, allowing the event to be cancelled

def after(self, delay_ms, callback=None, *args):
    """ wrapper for topLevel after function
        schedules the callback function to happen in x seconds
        returns an ID, allowing the event to be cancelled """
    return self.topLevel.after(delay_ms, callback, *args)

def afterCancel(

self, afterId)

wrapper for topLevel after_cancel function tries to cancel the specified callback

def afterCancel(self, afterId):
    """ wrapper for topLevel after_cancel function
        tries to cancel the specified callback """
    return self.after_cancel(afterId)

def afterIdle(

self, callback, *args)

wrapper for topLevel after_idle function schedules the callback function to happen in x seconds returns an ID, allowing the event to be cancelled

def afterIdle(self, callback, *args):
    """ wrapper for topLevel after_idle function
        schedules the callback function to happen in x seconds
        returns an ID, allowing the event to be cancelled """
    return self.after_idle(callback, *args)

def after_cancel(

self, afterId)

wrapper for topLevel after_cancel function tries to cancel the specified callback

def after_cancel(self, afterId):
    """ wrapper for topLevel after_cancel function
        tries to cancel the specified callback """
    return self.topLevel.after_cancel(afterId)

def after_idle(

self, callback, *args)

wrapper for topLevel after_idle function schedules the callback function to happen in x seconds returns an ID, allowing the event to be cancelled

def after_idle(self, callback, *args):
    """ wrapper for topLevel after_idle function
        schedules the callback function to happen in x seconds
        returns an ID, allowing the event to be cancelled """
    return self.topLevel.after_idle(callback, *args)

def appJarAbout(

self, menu=None)

def appJarAbout(self, menu=None):
    self.infoBox("About appJar",
                    "---\n" +
                    __copyright__ + "\n" +
                    "---\n\t" +
                    gui.SHOW_VERSION().replace("\n", "\n\t") + "\n" +
                    "---\n" +
                    gui.SHOW_PATHS() + "\n" +
                    "---")

def appJarHelp(

self, menu=None)

def appJarHelp(self, menu=None):
    self.infoBox("appJar Help", "For help, visit " + __url__)

def appendAutoEntry(

self, title, value)

def appendAutoEntry(self, title, value):
    entry = self.widgetManager.get(WIDGET_NAMES.Entry, title)
    try:
        entry.addWords(value)
    except AttributeError:
        gui.error("You can only append items to an AutoEntry, %s is not an AutoEntry.", title)

def bell(

self)

def bell(self):
    self.containerStack[0]['container'].bell()

def bindKey(

self, key, func, replace=False)

bind the specified key, to the specified function, for all widgets

def bindKey(self, key, func, replace=False):
    """ bind the specified key, to the specified function, for all widgets """
    if replace:
        try: self.unbindKey(key)
        except: pass
    # for now discard the Event...
    myF = self.MAKE_FUNC(func, key)
    binding = EventBinding(key, myF, self._getTopLevel(), menuBinding=False)
    try:
        self.widgetManager.add(WIDGET_NAMES.Bindings, binding.displayName, binding)
        binding.createBindings()
    except ItemLookupError:
        raise ItemLookupError('Unable to bind key ' + binding.displayName + ' - binding already exists')

def bindKeys(

self, keys, func)

bind the specified keys, to the specified function, for all widgets

def bindKeys(self, keys, func):
    """ bind the specified keys, to the specified function, for all widgets """
    for key in keys:
        self.bindKey(key, func)

def button(

self, title, value=None, *args, **kwargs)

simpleGUI - adds, sets & gets buttons all in one go

def button(self, title, value=None, *args, **kwargs):
    """ simpleGUI - adds, sets & gets buttons all in one go """
    widgKind = WIDGET_NAMES.Button
    image = kwargs.pop("image", None)
    icon = kwargs.pop("icon", None)
    name = kwargs.pop("label", kwargs.pop("name", None))
    try: self.widgetManager.verify(WIDGET_NAMES.Button, title)
    except: # widget exists
        if value is not None: self.setButton(title, value)
        button = self.getButton(title)
    else: # new widget
        kwargs = self._parsePos(kwargs.pop("pos", []), kwargs)
        if image is not None: button = self._buttonMaker(title, value, "image", image, *args, **kwargs)
        elif icon is not None: button = self._buttonMaker(title, value, "icon", icon, *args, **kwargs)
        elif name is not None: button = self._buttonMaker(title, value, "named", name, *args, **kwargs)
        else: button = self._buttonMaker(title, value, "button", None, *args, **kwargs)
    if len(kwargs) > 0:
        self._configWidget(title, widgKind, **kwargs)
    return button

def buttons(

self, names, funcs, **kwargs)

def buttons(self, names, funcs, **kwargs):
    kwargs = self._parsePos(kwargs.pop("pos", []), kwargs)
    self._addButtons(names, funcs, **kwargs)
    kwargs.pop('fill', False)
    if not isinstance(names[0], list):
        names = [names]
    for row in names:
        for title in row:
            self._configWidget(title, WIDGET_NAMES.Button, **kwargs)

def callback(

self, *args, **kwargs)

Shortner for threadCallback.

def callback(self, *args, **kwargs):
    """Shortner for threadCallback."""
    return self.threadCallback(*args, **kwargs)

def canvas(

self, title, *args, **kwargs)

simpleGUI - adds, sets & gets canases all in one go

def canvas(self, title, *args, **kwargs):
    """ simpleGUI - adds, sets & gets canases all in one go """
    widgKind = WIDGET_NAMES.Canvas
    submit = kwargs.pop("submit", None)
    _map = kwargs.pop("map", None)
    try: self.widgetManager.verify(widgKind, title)
    except: # widget exists
        # NB. no SETTER
        canvas = self.getCanvas(title)
    else: # new widget
        kwargs = self._parsePos(kwargs.pop("pos", []), kwargs)
        canvas = self._canvasMaker(title, *args, **kwargs)
    if submit is not None and _map is not None:
        self.setCanvasMap(title, submit, _map)
    else:
        gui.warn("Must specify a submit function when setting a canvas map: %s", title)
    if len(kwargs) > 0:
        self._configWidget(title, widgKind, **kwargs)
    self._configWidget(title, widgKind, **kwargs)
    return canvas

def changeAutoEntry(

self, title, value)

def changeAutoEntry(self, title, value):
    entry = self.widgetManager.get(WIDGET_NAMES.Entry, title)
    try:
        entry.changeWords(value)
    except AttributeError:
        gui.error("You can only change items in an AutoEntry, %s is not an AutoEntry.", title)

def changeLanguage(

self, language)

changes the language used by the GUI will iterate through all widgets and update their text as well as populate a translation dictionary for later lookups

def changeLanguage(self, language):
    """ changes the language used by the GUI
        will iterate through all widgets and update their text
        as well as populate a translation dictionary for later lookups """
    self._loadConfigParser()
    if not ConfigParser:
        self.error("Internationalisation not supported")
        return
    fileName = language.upper() + ".ini"
    gui.trace("Loading language file: %s", fileName)
    if not PYTHON2:
        try:
            with codecs.open(fileName, "r", "utf8") as langFile:
                self.configParser.read_file(langFile)
        except FileNotFoundError:
            self.error("Invalid language, file not found: %s", fileName)
            return
    else:
        try:
            try:
                with codecs.open(fileName, "r", "utf8") as langFile:
                    self.configParser.read_file(langFile)
            except AttributeError:
                with codecs.open(fileName, "r", "utf8") as langFile:
                    self.configParser.readfp(langFile)
        except IOError:
            self.error("Invalid language, file not found: %s", fileName)
            return
        except ParsingError:
            self.error("Translation failed - language file contains errors, ensure there is no whitespace at the beginning of any lines.")
            return
    gui.trace("Switching to: %s", language)
    self._language = language
    self.translations = {"POPUP":{}, "SOUND":{}, "EXTERNAL":{}}
    # loop through each section, get the relative set of widgets
    # change the text
    for section in self.configParser.sections():
        getWidgets = True
        section = section.upper()
        gui.trace("\tSection: %s", section)
        # convert the section title to its code
        if section == "CONFIG":
            # skip the config section (for now)
            gui.trace("\tSkipping CONFIG")
            continue
        elif section == "TITLE":
            kind = WIDGET_NAMES.SubWindow
        elif section.startswith("TOOLTIP-"):
            kind = "TOOLTIP"
            getWidgets = False
        elif section in ["SOUND", "EXTERNAL", "POPUP"]:
            for (key, val) in self.configParser.items(section):
                if section == "POPUP": val = val.strip().split("\n")
                self.translations[section][key] = val
                gui.trace("\t\t%s: %s", key, val)
            continue
        elif section == "MENUBAR":
            for (key, val) in self.configParser.items(section):
                key = key.strip().split("-")
                gui.trace("\t\t%s: %s", key, val)
                if len(key) == 1:
                    try:
                        self.renameMenu(key[0], val)
                    except:
                        self.warn("Invalid key")
                elif len(key) == 2:
                    try:
                        self.renameMenuItem(key[0], key[1], val)
                    except:
                        self.warn("Invalid key")
            continue
        else:
            try:
                kind = WIDGET_NAMES.getIgnoreCase(section)
            except Exception:
                self.warn("Invalid config section: %s", section)
                continue
        # if necessary, use the code to get the widget list
        if getWidgets:
            widgets = self.widgetManager.group(kind)
        if kind in [WIDGET_NAMES.Scale]:
            self.warn("No text is displayed in %s. Maybe it has a Label?", section)
            continue
        elif kind in [WIDGET_NAMES.TextArea, WIDGET_NAMES.Meter, WIDGET_NAMES.PieChart, WIDGET_NAMES.Tree]:
            self.warn("No text is displayed in %s", section)
            continue
        elif kind in [WIDGET_NAMES.name(WIDGET_NAMES.SubWindow)]:
            for (key, val) in self.configParser.items(section):
                gui.trace("\t\t%s: %s", key, val)
                if key.lower() == "appjar":
                    self.setTitle(val)
                elif key.lower() == "splash":
                    if self.splashConfig is not None:
                        gui.trace("\t\t Updated SPLASH to: %s", val)
                        self.splashConfig['text'] = val
                    else:
                        gui.trace("\t\t No SPLASH to update")
                elif key.lower() == "statusbar":
                    gui.trace("\tSetting STATUSBAR: %s", val)
                    self.setStatusbarHeader(val)
                else:
                    try:
                        widgets[key].title(val)
                    except KeyError:
                        self.warn("Invalid SUBWINDOW: %s", key)
        elif kind in [WIDGET_NAMES.ListBox]:
            for k in widgets.keys():
                lb = widgets[k]
                # convert data to a list
                if self.configParser.has_option(section, k):
                    data = self.configParser.get(section, k)
                else:
                    data = lb.DEFAULT_TEXT
                data = data.strip().split("\n")
                # tidy up the list
                data = [item.strip() for item in data if len(item.strip()) > 0]
                self.updateListBox(k, data)
        elif kind in [WIDGET_NAMES.SpinBox]:
            for k in widgets.keys():
                sb = widgets[k]
                # convert data to a list
                if self.configParser.has_option(section, k):
                    data = self.configParser.get(section, k)
                else:
                    data = sb.DEFAULT_TEXT
                data = data.strip().split("\n")
                # tidy up the list
                data = [item.strip() for item in data if len(item.strip()) > 0]
                self.changeSpinBox(k, data)
        elif kind in [WIDGET_NAMES.OptionBox]:
            for k in widgets.keys():
                ob = widgets[k]
                # convert data to a list
                if self.configParser.has_option(section, k):
                    data = self.configParser.get(section, k)
                else:
                    data = ob.DEFAULT_TEXT
                data = data.strip().split("\n")
                # tidy up the list
                data = [item.strip() for item in data if len(item.strip()) > 0]
                self.changeOptionBox(k, data)
        elif kind in [WIDGET_NAMES.RadioButton]:
            for (key, val) in self.configParser.items(section):
                gui.trace("\t\t%s: %s", key, val)
                keys = key.split("-")
                if len(keys) != 2:
                    self.warn("Invalid RADIOBUTTON key: %s", key)
                else:
                    try:
                        rbs = self.widgetManager.get(WIDGET_NAMES.RadioButton, keys[0])
                    except KeyError:
                        self.warn("Invalid RADIOBUTTON key: %s", keys[0])
                        continue
                    for rb in rbs:
                        if rb.DEFAULT_TEXT == keys[1]:
                            rb["text"] = val
                            break
        elif kind in [WIDGET_NAMES.TabbedFrame]:
            for (key, val) in self.configParser.items(section):
                gui.trace("\t\t%s: %s", key, val)
                keys = key.split("-")
                if len(keys) != 2:
                    self.warn("Invalid TABBEDFRAME key: %s", key)
                else:
                    try:
                        self.setTabText(keys[0], keys[1], val)
                    except ItemLookupError:
                        self.warn("Invalid TABBEDFRAME: %s with TAB: %s" , keys[0], keys[1])
        elif kind in [WIDGET_NAMES.Properties]:
            for (key, val) in self.configParser.items(section):
                gui.trace("\t\t%s: %s", key, val)
                keys = key.split("-")
                if len(keys) != 2:
                    self.warn("Invalid PROPERTIES key: %s", key)
                else:
                    try:
                        self.setPropertyText(keys[0], keys[1], val)
                    except ItemLookupError:
                        self.warn("Invalid PROPERTIES: %s", keys[0])
                    except KeyError:
                        self.warn("Invalid PROPERTY: %s", keys[1])
        elif kind == WIDGET_NAMES.Tree:
            for (key, val) in self.configParser.items(section):
                gui.trace("\t\t%s: %s", key, val)
                keys = key.split("-")
                if len(keys) != 2:
                    self.warn("Invalid GRID key: %s", key)
                else:
                    if keys[1] not in ["actionHeading", "actionButton", "addButton"]:
                        self.warn("Invalid GRID label: %s for GRID: %s", keys[1], keys[0])
                    else:
                        try:
                            self.confGrid(keys[0], keys[1], val)
                        except ItemLookupError:
                            self.warn("Invalid GRID: %s", keys[0])
        elif kind == self.PAGEDWINDOW:
            for (key, val) in self.configParser.items(section):
                gui.trace("\t\t%s: %s", key, val)
                keys = key.split("-")
                if len(keys) != 2:
                    self.warn("Invalid PAGEDWINDOW key: %s", key)
                else:
                    if keys[1] not in ["prevButton", "nextButton", "title"]:
                        self.warn("Invalid PAGEDWINDOW label: %s for PAGEDWINDOW: %s", keys[1], keys[0])
                    else:
                        try:
                            widgets[keys[0]].config(**{keys[1]:val})
                        except KeyError:
                            self.warn("Invalid PAGEDWINDOW: %s", keys[0])
        elif kind == WIDGET_NAMES.Entry:
            for k in widgets.keys():
                ent = widgets[k]
                if self.configParser.has_option(section, k):
                    data = self.configParser.get(section, k)
                else:
                    data = ent.DEFAULT_TEXT
                gui.trace("\t\t%s: %s", k, data)
                self.setEntryDefault(k, data)
        elif kind in [WIDGET_NAMES.Image]:
            for k in widgets.keys():
                if self.configParser.has_option(section, k):
                    data = str(self.configParser.get(section, k))
                    try:
                        self.setImage(k, data)
                        gui.trace("\t\t%s: %s", k, data)
                    except:
                        self.error("Failed to update image: %s to: %s", k, data)
                else:
                    gui.trace("No translation for: %s", k)
        elif kind in [WIDGET_NAMES.Label, WIDGET_NAMES.Button, WIDGET_NAMES.CheckBox, WIDGET_NAMES.Message,
                        WIDGET_NAMES.Link, WIDGET_NAMES.LabelFrame, self.TOGGLEFRAME]:
            for k in widgets.keys():
                widg = widgets[k]
                # skip validation labels - we don't need to translate them
                try:
                    if kind == WIDGET_NAMES.Label and widg.isValidation:
                        gui.trace("\t\t%s: skipping, validation label", k)
                        continue
                except:
                    pass
                if self.configParser.has_option(section, k):
                    data = str(self.configParser.get(section, k))
                else:
                    data = widg.DEFAULT_TEXT
                gui.trace("\t\t%s: %s", k, data)
                widg.config(text=data)
        elif kind == WIDGET_NAMES.Toolbar:
            for k in widgets.keys():
                but = widgets[k]
                if but.image is None:
                    if self.configParser.has_option(section, k):
                        data = str(self.configParser.get(section, k))
                    else:
                        data = but.DEFAULT_TEXT
                    gui.trace("\t\t%s: %s", k, data)
                    but.config(text = data)
        elif kind == "TOOLTIP":
            try:
                kind = WIDGET_NAMES.name(WIDGET_NAMES.getIgnoreCase(section.split("-")[1]))
                func = getattr(self, "set"+kind+"Tooltip")
            except KeyError:
                self.warn("Invalid config section: TOOLTIP-%s", section)
                return
            gui.trace("Parsing TOOLTIPs for: %s", kind)
            for (key, val) in self.configParser.items(section):
                try:
                    func(key, val)
                except ItemLookupError:
                    self.warn("Invalid TOOLTIP for: %s, with key: %s", kind, key)
                    continue
        else:
            self.warn("Unsupported widget: %s", section)
            continue

def changeOptionBox(

self, title, options, index=None, callFunction=False)

Changes the entire contents of the named OptionBox ref: http://www.prasannatech.net/2009/06/tkinter-optionmenu-changing-choices.html

:param title: the OptionBox to change :param options: the new values to put in the OptionBox :param index: an optional initial value to select :param callFunction: whether to generate an event to notify that the widget has changed :returns: None :raises ItemLookupError: if the title can't be found

def changeOptionBox(self, title, options, index=None, callFunction=False):
    """ Changes the entire contents of the named OptionBox
    ref: http://www.prasannatech.net/2009/06/tkinter-optionmenu-changing-choices.html
    :param title: the OptionBox to change
    :param options: the new values to put in the OptionBox
    :param index: an optional initial value to select
    :param callFunction: whether to generate an event to notify that the widget has changed
    :returns: None
    :raises ItemLookupError: if the title can't be found
    """
    # get the optionBox & associated var
    box = self.widgetManager.get(WIDGET_NAMES.OptionBox, title)
    # tidy up list and get max size
    maxSize, options = self._configOptionBoxList(title, options, "normal")
    # warn if new options bigger
    if maxSize > box.maxSize:
        self.warn("The new options are wider then the old ones: %s > %s", maxSize, box.maxSize)
    if box.kind == "ticks":
        self._buildTickOptionBox(title, box, options)
    else:
        # delete the current options
        box['menu'].delete(0, 'end')
        # add the new items
        for option in options:
            box["menu"].add_command(
                label=option, command=lambda temp=option: box.setvar(
                    box.cget("textvariable"), value=temp))
        with PauseCallFunction(callFunction, box):
            box.var.set(options[0])
    box.options = options
    # disable any separators
    self._disableOptionBoxSeparators(box)
    # select the specified option
    self.setOptionBox(title, index, callFunction=False, override=True)

def changeSpinBox(

self, title, vals, reverse=True)

def changeSpinBox(self, title, vals, reverse=True):
    spin = self.widgetManager.get(WIDGET_NAMES.SpinBox, title)
    if spin.isRange:
        self.warn("Can't convert %s RangeSpinBox to SpinBox", title)
    else:
        self._populateSpinBox(spin, vals, reverse)
        self.setSpinBoxPos(title, 0)

def check(

self, title, value=None, *args, **kwargs)

simpleGUI - shortner for checkBox()

def check(self, title, value=None, *args, **kwargs):
    """ simpleGUI - shortner for checkBox() """
    return self.checkBox(title, value, *args, **kwargs)

def checkBox(

self, title, value=None, *args, **kwargs)

adds, sets & gets checkBoxes all in one go

def checkBox(self, title, value=None, *args, **kwargs):
    """ adds, sets & gets checkBoxes all in one go """
    widgKind = WIDGET_NAMES.CheckBox
    callFunction = kwargs.pop("callFunction", True)
    text = kwargs.pop("text", None)
    try: self.widgetManager.verify(widgKind, title)
    except: #widget exists
        if value is not None: self.setCheckBox(title, ticked=value, callFunction=callFunction)
        check = self.getCheckBox(title)
    else: # new widget
        kwargs = self._parsePos(kwargs.pop("pos", []), kwargs)
        check = self._checkBoxMaker(title, *args, **kwargs)
        if value is not None: self.setCheckBox(title, value)
    if text is not None: self.setCheckBoxText(title, text)
    if len(kwargs) > 0:
        self._configWidget(title, widgKind, **kwargs)
    return check

def cleanseWidgets(

self, widget)

def cleanseWidgets(self, widget):
    widgType = gui.GET_WIDGET_CLASS(widget)
    gui.trace("Attempting to cleanse: %s", widgType)
    # make sure we've cleansed any children first
    for child in widget.winfo_children():
        self.cleanseWidgets(child)
    if hasattr(widget, 'APPJAR_TYPE'):
        widgType = widget.APPJAR_TYPE
        widgName = WIDGET_NAMES.name(widgType)
        gui.trace("Cleansing: %s", widgName)
        if widgType not in [WIDGET_NAMES.Tab, WIDGET_NAMES.Page]:
            if not self.widgetManager.destroyWidget(widgType, widget):
                self.warn("Unable to destroy %s, during cleanse - destroy returned False", widgName)
            # must clear the frameLabel's label as well
            if widgType == WIDGET_NAMES.FrameLabel:
                gui.trace("Also Cleansing: %s", WIDGET_NAMES.name(WIDGET_NAMES.Label))
                if not self.widgetManager.destroyWidget(WIDGET_NAMES.Label, widget):
                    self.warn("Unable to destroy %s, during cleanse - destroy returned False", WIDGET_NAMES.Label)
        else:
            self.trace("Skipped %s, cleansed by parent", widgType)
        # need to remove if a container
        if widgName in WIDGET_NAMES.containers:
            self.trace("Destroying container: %s", widgName)
            self.widgetManager.destroyContainer(WIDGET_NAMES.ContainerLog, widget)
     elif widgType in ('CanvasDnd', 'ValidationLabel', 'TabBorder', 'TabContainer', 'TabText', 'BgLabel') or hasattr(widget, 'SKIP_CLEANSE'):
    elif widgType in ('CanvasDnd', 'ValidationLabel', 'Grip',
                        'TabBorder', 'TabContainer', 'TabText', 'BgLabel') \
                        or widget.__dict__.get('SKIP_CLEANSE', False):
        pass # not logged in WidgetManager
    else:
        self.warn("Unable to destroy %s, during cleanse - NO APPJAR TYPE", gui.GET_WIDGET_CLASS(widget))

def clearAllCheckBoxes(

self, callFunction=False)

def clearAllCheckBoxes(self, callFunction=False):
    for cb in self.widgetManager.group(WIDGET_NAMES.CheckBox):
        self.setCheckBox(cb, ticked=False, callFunction=callFunction)

def clearAllDatePickers(

self, callFunction=False)

def clearAllDatePickers(self, callFunction=False):
    for k in self.widgetManager.group(WIDGET_NAMES.DatePicker):
        self.clearDatePicker(k, callFunction)

def clearAllEntries(

self, callFunction=False)

def clearAllEntries(self, callFunction=False):
    for entry in self.widgetManager.group(WIDGET_NAMES.Entry, group=WidgetManager.VARS):
        self.clearEntry(entry, callFunction=callFunction, setFocus=False)

def clearAllLabels(

self)

def clearAllLabels(self):
    for lb in self.widgetManager.group(WIDGET_NAMES.Label):
        self.clearLabel(lb)

def clearAllListBoxes(

self, callFunction=False)

def clearAllListBoxes(self, callFunction=False):
    for lb in self.widgetManager.group(WIDGET_NAMES.ListBox):
        self.clearListBox(lb, callFunction)

def clearAllOptionBoxes(

self, callFunction=False)

Convenience function to clear all OptionBoxes in the GUI Will simply call clearOptionBox on each OptionBox/TickOptionBox

:param callFunction: whether to generate an event to notify that the widget has changed :returns: None

def clearAllOptionBoxes(self, callFunction=False):
    """ Convenience function to clear all OptionBoxes in the GUI
    Will simply call clearOptionBox on each OptionBox/TickOptionBox
    :param callFunction: whether to generate an event to notify that the widget has changed
    :returns: None
    """
    for k in self.widgetManager.group(WIDGET_NAMES.OptionBox):
        self.clearOptionBox(k, callFunction)

def clearAllProperties(

self, callFunction=False)

def clearAllProperties(self, callFunction=False):
    props = {}
    for k in self.widgetManager.group(WIDGET_NAMES.Properties):
        self.clearProperties(k, callFunction)

def clearAllRadioButtons(

self, callFunction=False)

def clearAllRadioButtons(self, callFunction=False):
    for rb in self.widgetManager.group(WIDGET_NAMES.RadioButton, group=WidgetManager.VARS):
        self.setRadioButton(rb, self.widgetManager.get(WIDGET_NAMES.RadioButton, rb, group=WidgetManager.VARS).startVal, callFunction=callFunction)

def clearAllScales(

self, callFunction=False)

def clearAllScales(self, callFunction=False):
    for sc in self.widgetManager.group(WIDGET_NAMES.Scale):
        self.setScale(sc, self.widgetManager.get(WIDGET_NAMES.Scale, sc).cget("from"), callFunction=callFunction)

def clearAllSpinBoxes(

self, callFunction=False)

def clearAllSpinBoxes(self, callFunction=False):
    for sb in self.widgetManager.group(WIDGET_NAMES.SpinBox):
        self.setSpinBoxPos(sb, 0, callFunction=callFunction)

def clearAllTextAreas(

self, callFunction=False)

Convenience function to clear all TextAreas in the GUI Will simply call clearTextArea on each TextArea

:param callFunction: whether to generate an event to notify that the widget has changed :returns: None

def clearAllTextAreas(self, callFunction=False):
    """ Convenience function to clear all TextAreas in the GUI
    Will simply call clearTextArea on each TextArea
    :param callFunction: whether to generate an event to notify that the widget has changed
    :returns: None
    """
    for ta in self.widgetManager.group(WIDGET_NAMES.TextArea):
        self.clearTextArea(ta, callFunction=callFunction)

def clearCanvas(

self, title)

def clearCanvas(self, title):
    self.widgetManager.get(WIDGET_NAMES.Canvas, title).delete("all")

def clearDatePicker(

self, title, callFunction=True)

def clearDatePicker(self, title, callFunction=True):
    self.widgetManager.get(WIDGET_NAMES.DatePicker, title)
    self.setOptionBox(title + "_DP_YearOptionBox", 0, callFunction)
    self.setOptionBox(title + "_DP_MonthOptionBox", 0, callFunction)
    self.setOptionBox(title + "_DP_DayOptionBox", 0, callFunction)

def clearEntry(

self, name, callFunction=True, setFocus=True)

def clearEntry(self, name, callFunction=True, setFocus=True):
    var = self.widgetManager.get(WIDGET_NAMES.Entry, name, group=WidgetManager.VARS)
    # now call function
    with PauseCallFunction(callFunction, var, False):
        var.set("")
    self._updateEntryDefault(name, mode="clear")
    if setFocus: self.setFocus(name)

def clearImageCache(

self)

def clearImageCache(self):
    self.widgetManager.clear(WIDGET_NAMES.ImageCache)

def clearLabel(

self, name)

def clearLabel(self, name):
    self.setLabel(name, "")

def clearListBox(

self, title, callFunction=True)

def clearListBox(self, title, callFunction=True):
    lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title)
    lb.selection_clear(0, END)
    lb.delete(0, END)  # clear
    if callFunction and hasattr(lb, 'cmd'):
        lb.cmd()

def clearMessage(

self, title)

def clearMessage(self, title):
    self.setMessage(title, "")

def clearMicroBit(

self, title)

def clearMicroBit(self, title):
    self.widgetManager.get(WIDGET_NAMES.MicroBit, title).clear()

def clearOptionBox(

self, title, callFunction=True)

Deselects any items selected in the named OptionBox If a TickOptionBox, all items will be set to False (unticked)

:param title: the OptionBox to change :param callFunction: whether to generate an event to notify that the widget has changed :returns: None :raises ItemLookupError: if the title can't be found

def clearOptionBox(self, title, callFunction=True):
    """ Deselects any items selected in the named OptionBox
    If a TickOptionBox, all items will be set to False (unticked)
    :param title: the OptionBox to change
    :param callFunction: whether to generate an event to notify that the widget has changed
    :returns: None
    :raises ItemLookupError: if the title can't be found
    """
    box = self.widgetManager.get(WIDGET_NAMES.OptionBox, title)
    if box.kind == "ticks":
        # loop through each tick, set it to False
        ticks = self.widgetManager.get(WIDGET_NAMES.TickOptionBox, title, group=WidgetManager.VARS)
        for k in ticks:
            self.setOptionBox(title, k, False, callFunction=callFunction)
    else:
        self.setOptionBox(title, 0, callFunction=callFunction, override=True)

def clearProperties(

self, title, callFunction=True)

def clearProperties(self, title, callFunction=True):
    props = self.widgetManager.get(WIDGET_NAMES.Properties, title)
    props.clearProperties(callFunction)

def clearSpinBox(

self, title, callFunction=False)

def clearSpinBox(self, title, callFunction=False):
    self.setSpinBoxPos(title, 0, callFunction=callFunction)

def clearStatusbar(

self, field=None)

def clearStatusbar(self, field=None):
    if self.hasStatus:
        if field is None:
            for status in self._statusFields:
                status.config(text=self._getFormatStatus(""))
        elif field >= 0 and field < len(self._statusFields):
            self._statusFields[field].config(text=self._getFormatStatus(""))
        else:
            raise Exception("Invalid status field: " + str(field) +
                            ". Must be between 0 and " + str(len(self._statusFields) - 1))

def clearTextArea(

self, title, callFunction=True)

Removes all text from the specified TextArea

:param title: the TextArea to change :param callFunction: whether to generate an event to notify that the widget has changed :returns: None :raises ItemLookupError: if the title can't be found

def clearTextArea(self, title, callFunction=True):
    """ Removes all text from the specified TextArea
    :param title: the TextArea to change
    :param callFunction: whether to generate an event to notify that the widget has changed
    :returns: None
    :raises ItemLookupError: if the title can't be found
    """
    ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title)
    ta.pauseCallFunction(callFunction)
    # in case it's disabled
    _state = ta.cget('state')
    ta.config(state='normal')
    ta.delete('1.0', END)
    ta.config(state=_state)
    ta.resumeCallFunction()

def clearTree(

self, title)

def clearTree(self, title):
    tree = self.widgetManager.get(WIDGET_NAMES.Tree, title)
    tree.destroy()
    tree.update()

def colourBox(

self, colour='#ff0000', parent=None)

def colourBox(self, colour='#ff0000', parent=None):
    self.topLevel.update_idletasks()
    if parent is None:
        col = askcolor(colour)
    else:
        parent = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent)
        opts = {"parent": parent}
        col = askcolor(colour, **opts)
    if col[1] is None:
        return None
    else:
        return col[1]

def combo(

self, title, value=None, *args, **kwargs)

shortner for optionBox()

def combo(self, title, value=None, *args, **kwargs):
    """ shortner for optionBox() """
    return self.optionBox(title, value, *args, **kwargs)

def confGrid(

self, title, field, value)

def confGrid(self, title, field, value):
    return self.confTable(title, field, value)

def confTable(

self, title, field, value)

def confTable(self, title, field, value):
    grid = self.widgetManager.get(WIDGET_NAMES.Table, title)
    kw = {field:value}
    grid.config(**kw)

def config(

self, **kwargs)

def config(self, **kwargs):
    self.configure(**kwargs)

def configure(

self, **kwargs)

def configure(self, **kwargs):
    title = kwargs.pop("title", None)
    icon = kwargs.pop("icon", None)
    transparency = kwargs.pop("transparency", None)
    visible = kwargs.pop("visible", None)
    top = kwargs.pop("top", None)
    padding = kwargs.pop("padding", None)
    inPadding = kwargs.pop("inPadding", None)
    guiPadding = kwargs.pop("guiPadding", None)
    size = kwargs.pop("size", None)
    location = kwargs.pop("location", None)
    fullscreen = kwargs.pop("fullscreen", None)
    resizable = kwargs.pop("resizable", None)
    sticky = kwargs.pop("sticky", None)
    stretch = kwargs.pop("stretch", None)
    expand = kwargs.pop("expand", None)
    row = kwargs.pop("row", None)
    colspan = kwargs.pop("colspan", None)
    rowspan = kwargs.pop("rowspan", None)
    fg = kwargs.pop("fg", None)
    bg = kwargs.pop("bg", None)
    font = kwargs.pop("font", None)
    buttonFont = kwargs.pop("buttonFont", None)
    labelFont = kwargs.pop("labelFont", None)
    inputFont = kwargs.pop("inputFont", None)
    statusFont = kwargs.pop("statusFont", None)
    ttkTheme = kwargs.pop("ttkTheme", None)
    editMenu = kwargs.pop("editMenu", None)
    # two possible names
    stopFunction = kwargs.pop("stop", kwargs.pop("stopFunction", None))
    startFunction = kwargs.pop("start", kwargs.pop("startFunction", None))
    fastStop = kwargs.pop("fastStop", None)
    enterKey = kwargs.pop("enterKey", None)
    logLevel = kwargs.pop("log", kwargs.pop("logLevel", None))
    logFile = kwargs.pop("file", kwargs.pop("logFile", None))
    language = kwargs.pop("language", None)
    for k, v in kwargs.items():
        gui.error("Invalid config parameter: %s, %s", k, v)
    if title is not None: self.title = title
    if icon is not None: self.icon = icon
    if transparency is not None: self.transparency = transparency
    if visible is not None: self.visible = visible
    if top is not None: self.top = top
    if padding is not None: self.padding = padding
    if inPadding is not None: self.inPadding = inPadding
    if guiPadding is not None: self.guiPadding = guiPadding
    if size is not None: self.size = size
    if location is not None: self.location = location
    if fullscreen is not None: self.fullscreen = fullscreen
    if resizable is not None: self.resizable = resizable
    if sticky is not None: self.sticky = sticky
    if expand is not None: self.expand = expand
    if stretch is not None: self.stretch = stretch
    if row is not None: self.row = row
    if rowspan is not None: self.rowspan = rowspan
    if colspan is not None: self.colspan = colspan
    if fg is not None: self.fg = fg
    if bg is not None: self.bg = bg
    if font is not None: self.font = font
    if labelFont is not None: self.labelFont = labelFont
    if buttonFont is not None: self.buttonFont = buttonFont
    if inputFont is not None: self.inputFont = inputFont
    if statusFont is not None: self.statusFont = statusFont
    if ttkTheme is not None: self.ttkTheme = ttkTheme
    if editMenu is not None: self.editMenu = editMenu
    if stopFunction is not None: self.stopFunction = stopFunction
    if startFunction is not None: self.startFunction = startFunction
    if fastStop is not None: self.fastStop = fastStop
    if enterKey is not None: self.enterKey = enterKey
    if logLevel is not None: self.logLevel = logLevel
    if logFile is not None: self.logFile = logFile
    if language is not None: self.language = language

def configureAllWidgets(

self, kind, option, value)

def configureAllWidgets(self, kind, option, value):
    items = list(self.widgetManager.group(kind))
    self.configureWidgets(kind, items, option, value)

def configureWidget(

self, kind, name, option, value, key=None, deprecated=False)

def configureWidget(self, kind, name, option, value, key=None, deprecated=False):
    gui.trace("Configuring: %s of %s with %s of %s", name, kind, option, value)
    # warn about deprecated functions
    if deprecated:
        self.warn("Deprecated config function (%s) used for %s -> %s use %s deprecated", option, WIDGET_NAMES.name(kind), name, deprecated)
    # will return multiple items if radio button...
    items = self._getWidgetList(kind, name, limit=option in ['change', 'command'])
    # loop through each item, and try to reconfigure it
    # this will often fail - widgets have varied config options
    for item in items:
        try:
            if option == 'background':
                gui.SET_WIDGET_BG(item, value, True)
            elif option == 'foreground':
                gui.SET_WIDGET_FG(item, value, True)
            elif option == 'disabledforeground':
                item.config(disabledforeground=value)
            elif option == 'disabledbackground':
                item.config(disabledbackground=value)
            elif option == 'activeforeground':
                item.config(activeforeground=value)
            elif option == 'activebackground':
                item.config(activebackground=value)
            elif option == 'inactiveforeground':
                if kind in [WIDGET_NAMES.TabbedFrame, WIDGET_NAMES.Table]:
                    item.config(inactiveforeground=value)
                else:
                    self.warn("Error configuring %s: can't set inactiveforeground", name )
            elif option == 'inactivebackground':
                if kind in [WIDGET_NAMES.TabbedFrame, WIDGET_NAMES.Table]:
                    item.config(inactivebackground=value)
                else:
                    self.warn("Error configuring %s: can't set inactivebackground", name)
            elif option == 'width':
                item.config(width=value)
            elif option == 'height':
                item.config(height=value)
            elif option == 'state':
                # make entries readonly - can still copy/paste
                but = None
                if kind == WIDGET_NAMES.Entry:
                    if value == "disabled" and hasattr(item, 'but'):
                        but = item.but
                        item.unbind("<Button-1>")
                        value = "readonly"
                    elif value == 'normal' and hasattr(item, 'but') and item.cget('state') != 'normal':
                        but = item.but
                        item.bind("<Button-1>", item.click_command, "+")
                if self.ttkFlag:
                    gui.trace("%s configured with ttk state %s", name, value)
                    item.state([value])
                    if but is not None: but.state([value])
                else:
                    item.config(state=value)
                    if but is not None: but.config(state=value)
            elif option == 'relief':
                item.config(relief=value)
            elif option == 'style':
                if self.ttkFlag:
                    gui.trace("%s configured with ttk style %s", name, value)
                    item.config(style=value)
                else:
                    self.warn("Error configuring %s: can't set ttk style, not in ttk mode.", name)
            elif option in ['align', 'anchor']:
                if kind == WIDGET_NAMES.Entry or gui.GET_WIDGET_CLASS(item) == 'SelectableLabel':
                    if value == W: value = LEFT
                    elif value == E: value = RIGHT
                    item.config(justify=value)
                elif kind == WIDGET_NAMES.LabelFrame:
                    item.config(labelanchor=value)
                else:
                    if value == LEFT: value = "w"
                    elif value == RIGHT: value = "e"
                    item.config(anchor=value)
            elif option == 'cursor':
                item.config(cursor=value)
            elif option == 'tooltip':
                self._addTooltip(item, value)
            elif option == 'disableTooltip':
                self._disableTooltip(item)
            elif option == 'enableTooltip':
                self._enableTooltip(item)
            elif option == "focus":
                item.focus_set()
                if kind == WIDGET_NAMES.Entry:
                    if not self.ttkFlag:
                        item.icursor(END)
                        item.xview(END)
                    else:
                        item.icursor(END)
                        item.xview(len(item.get()))
            # event bindings
            elif option == 'over':
                self._bindOverEvent(kind, name, item, value, option, key)
            elif option == 'drag':
                self._bindDragEvent(kind, name, item, value, option, key)
            elif option in ['command', "change", "submit"]:
                self._bindEvent(kind, name, item, value, option, key)
            elif option == 'sticky':
                info = {}
                # need to reposition the widget in its grid
                if self._widgetHasContainer(kind, item):
                    # pack uses LEFT & RIGHT & BOTH
                    info["side"] = value
                    if value.lower() == "both":
                        info["expand"] = 1
                        info["side"] = "right"
                    else:
                        info["expand"] = 0
                else:
                    # grid uses E+W
                    if value.lower() == "left":
                        side = W
                    elif value.lower() == "right":
                        side = E
                    elif value.lower() == "both":
                        side = W + E
                    else:
                        side = value.upper()
                    info["sticky"] = side
                self._repackWidget(item, info)
            elif option == 'padding':
                if value[1] is None:
                    item.config(padx=value[0][0], pady=value[0][1])
                else:
                    item.config(padx=value[0], pady=value[1])
            elif option == 'ipadding':
                if value[1] is None:
                    item.config(ipadx=value[0][0], ipady=value[0][1])
                else:
                    item.config(ipadx=value[0], ipady=value[1])
            elif option == 'rightClick':
                self._bindRightClick(item, value)
            elif option == 'internalDrop':
                self._registerInternalDropTarget(item, value)
            elif option == 'internalDrag':
                self._registerInternalDragSource(kind, name, item, value)
            elif option == 'externalDrop':
                self._registerExternalDropTarget(name, item, value[0], value[1])
            elif option == 'externalDrag':
                self._registerExternalDragSource(name, item, value)
        except TclError as e:
            self.warn("Error configuring %s: %s", name, str(e))

def configureWidgets(

self, kind, names, option, value)

def configureWidgets(self, kind, names, option, value):
    if not isinstance(names, list):
        self.configureWidget(kind, names, option, value)
    else:
        for widg in names:
            # incase 2D array, eg. buttons
            if isinstance(widg, list):
                for widg2 in widg:
                    self.configureWidget(kind, widg2, option, value)
            else:
                self.configureWidget(kind, widg, option, value)

def confirmHideSubWindow(

self, title)

def confirmHideSubWindow(self, title):
    self.hideSubWindow(title, True)

def convertJpgToBmp(

self, image)

def convertJpgToBmp(self, image):
    self._loadNanojpeg()
    if nanojpeg is False:
        raise Exception(
            "nanojpeg library not found, unable to display jpeg files: " + image)
    elif sys.version_info < (2, 7):
        raise Exception(
            "JPG images only supported in python 2.7+: " + image)
    else:
        # read the image into an array of bytes
        with open(image, 'rb') as inFile:
            buf = array.array(str('B'), inFile.read())
        # init the translator, and decode the array of bytes
        nanojpeg.njInit()
        nanojpeg.njDecode(buf, len(buf))
        # determine a file name & type
        if nanojpeg.njIsColor():
             fileName = image.split('.jpg', 1)[0] + '.ppm'
            param = 6
        else:
             fileName = image.split('.jpg', 1)[0] + '.pgm'
             fileName = "test3.pgm"
            param = 5
        # create a string, starting with the header
        val = "P%d\n%d %d\n255\n" % (
            param, nanojpeg.njGetWidth(), nanojpeg.njGetHeight())
        # append the bytes, converted to chars
        val = str(val) + str('').join(map(chr, nanojpeg.njGetImage()))
        # release any stuff
        nanojpeg.njDone()
        photo = PhotoImage(data=val)
        return photo

def countFrames(

self, title)

def countFrames(self, title):
    return self.widgetManager.get(WIDGET_NAMES.FrameStack, title).getNumFrames()

def createMenu(

self, title, tearable=False, showInBar=True)

def createMenu(self, title, tearable=False, showInBar=True):
    self.widgetManager.verify(WIDGET_NAMES.Menu, title)
    self._initMenu()
    if title == "WIN_SYS" and self.platform != self.WINDOWS:
        self.warn("The WIN_SYS menu is specific to Windows")
        return None
    if self.platform == self.MAC and tearable:
        self.warn("Tearable menus (%s) not supported on MAC", title)
        tearable = False
    theMenu = Menu(self.menuBar, tearoff=tearable)
    if showInBar:
        self.menuBar.add_cascade(label=title, menu=theMenu)
    self.widgetManager.add(WIDGET_NAMES.Menu, title, theMenu)
    return theMenu

def createRightClickMenu(

self, title, showInBar=False)

def createRightClickMenu(self, title, showInBar=False):
    men = self.createMenu(title, False, showInBar)
    men.bind("<FocusOut>", lambda e: men.unpost())
    return men

def critical(

message, *args)

wrapper for logMessage - setting level to CRITICAL

@staticmethod
def critical(message, *args):
    """ wrapper for logMessage - setting level to CRITICAL """
    gui.logMessage(message, "CRITICAL", *args)

def date(

self, title, value=None, *args, **kwargs)

simpleGUI - shortner for datePicker()

def date(self, title, value=None, *args, **kwargs):
    """ simpleGUI - shortner for datePicker() """
    return self.datePicker(title, value, *args, **kwargs)

def datePicker(

self, title, value=None, *args, **kwargs)

simpleGUI - adds, sets & gets datePickers all in one go

def datePicker(self, title, value=None, *args, **kwargs):
    """ simpleGUI - adds, sets & gets datePickers all in one go """
    widgKind = WIDGET_NAMES.DatePicker
    change = kwargs.pop("change", None)
    toValue = kwargs.pop("toValue", None)
    try: self.widgetManager.verify(widgKind, title)
    except: # widget exists
        dp = self.getDatePicker(title)
    else: # new widget
        kwargs = self._parsePos(kwargs.pop("pos", []), kwargs)
        dp = self.addDatePicker(title, *args, **kwargs)
    if value is not None:
        if toValue is None: self.setDatePicker(title, value)
        else: self.setDatePickerRange(title, startYear=value, endYear=toValue)
    if change is not None: self.setDatePickerChangeFunction(title, change)
    if len(kwargs) > 0:
        self._configWidget(title, widgKind, **kwargs)
    return dp

def debug(

message, *args)

wrapper for logMessage - setting level to DEBUG

@staticmethod
def debug(message, *args):
    """ wrapper for logMessage - setting level to DEBUG """
    gui.logMessage(message, "DEBUG", *args)

def decreaseButtonFont(

self)

def decreaseButtonFont(self):
    self.setButtonFont(size=self._buttonFont['size'] - 1)

def decreaseFont(

self)

def decreaseFont(self):
    self.decreaseLabelFont()
    self.decreaseButtonFont()

def decreaseLabelFont(

self)

def decreaseLabelFont(self):
    self.setLabelFont(size=self._labelFont['size'] - 1)

def deleteAllGridRows(

self, title)

def deleteAllGridRows(self, title):
    return self.deleteAllTableRows(title)

def deleteAllTableRows(

self, title)

def deleteAllTableRows(self, title):
    grid = self.widgetManager.get(WIDGET_NAMES.Table, title)
    grid.deleteAllRows()

def deleteGridColumn(

self, title, columnNumber)

def deleteGridColumn(self, title, columnNumber):
    return self.deleteTableColumn(title, columnNumber)

def deleteGridRow(

self, title, rowNum)

def deleteGridRow(self, title, rowNum):
    return self.deleteTableRow(title, rowNum)

def deleteMenuItem(

self, title, item)

def deleteMenuItem(self, title, item):
    theMenu = self.widgetManager.get(WIDGET_NAMES.Menu, title)
    try:
        bindingName = theMenu.entrycget(item, 'accelerator')
        theMenu.delete(item)
        self.widgetManager.get(WIDGET_NAMES.Bindings, bindingName).removeBindings()
        self.widgetManager.remove(WIDGET_NAMES.Bindings, bindingName)
    except TclError:
        gui.error("Unable to delete menu item: %s, in menu: %s - item not found", item, title)

def deleteOptionBox(

self, title, index)

Deleted the specified item from the named OptionBox

:param title: the OptionBox to change :param inde: the value to delete - either a numeric index, or the text of an item :returns: None :raises ItemLookupError: if the title can't be found

def deleteOptionBox(self, title, index):
    """ Deleted the specified item from the named OptionBox
    :param title: the OptionBox to change
    :param inde: the value to delete - either a numeric index, or the text of an item
    :returns: None
    :raises ItemLookupError: if the title can't be found
    """
    self.widgetManager.check(WIDGET_NAMES.OptionBox, title, group=WidgetManager.VARS)
    self.setOptionBox(title, index, value=None, override=True)

def deleteProperty(

self, title, prop)

def deleteProperty(self, title, prop):
    props = self.widgetManager.get(WIDGET_NAMES.Properties, title)
    props.addProperty(prop, None, callFunction=False)

def deleteTabbedFrameTab(

self, title, tab)

def deleteTabbedFrameTab(self, title, tab):
    nb = self.widgetManager.get(WIDGET_NAMES.TabbedFrame, title)
    self.cleanseWidgets(nb.getTab(tab))
    nb.deleteTab(tab)

def deleteTableColumn(

self, title, columnNumber)

deletes the specified column from the specified table

def deleteTableColumn(self, title, columnNumber):
    ''' deletes the specified column from the specified table '''
    grid = self.widgetManager.get(WIDGET_NAMES.Table, title)
    grid.deleteColumn(columnNumber)

def deleteTableRow(

self, title, rowNum)

def deleteTableRow(self, title, rowNum):
    grid = self.widgetManager.get(WIDGET_NAMES.Table, title)
    grid.deleteRow(rowNum)

def deselectAllListItems(

self, title, callFunction=False)

def deselectAllListItems(self, title, callFunction=False):
    lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title)
    lb.selection_clear(0, END)
    if callFunction and hasattr(lb, 'cmd'):
        lb.cmd()

def deselectListItemAtPos(

self, title, pos, callFunction=False)

def deselectListItemAtPos(self, title, pos, callFunction=False):
    lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title)
    if lb.size() == 0:
        gui.warn("No items in list: %s, unable to deselect item at pos: %s", title, pos)
        return False
    if pos < 0 or pos > lb.size() - 1:
        gui.warn("Invalid list position: %s for list: %s (max: %s)", pos, title, lb.size()-1)
        return False
    lb.selection_clear(pos)
    if callFunction and hasattr(lb, 'cmd'):
        lb.cmd()
    self.topLevel.update_idletasks()
    return True

def destroyAllSubWindows(

self)

def destroyAllSubWindows(self):
    gui.trace("Destroying all SubWindows")
    keys = list(self.widgetManager.group(WIDGET_NAMES.SubWindow).keys())
    for k in keys:
        gui.trace("Destroying SubWindow: %s", k)
        wi = self.widgetManager.get(WIDGET_NAMES.SubWindow, k)
        self.cleanseWidgets(wi)
    # access has widgets stored in the standard widget store
    self.accessMade = False

def destroySubWindow(

self, title)

def destroySubWindow(self, title):
    gui.trace("Destroying SubWindow %s", title)
    tl = self.widgetManager.get(WIDGET_NAMES.SubWindow, title)
    tl.prepDestroy()
    # get rid of all the kids!
    self.cleanseWidgets(tl)

def directoryBox(

self, title=None, dirName=None, parent=None)

def directoryBox(self, title=None, dirName=None, parent=None):
    self.topLevel.update_idletasks()
    options = {}
    options['initialdir'] = dirName
    options['title'] = title
    options['mustexist'] = False
    if parent is not None:
        options["parent"] = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent)
    fileName = filedialog.askdirectory(**options)
    if fileName == "":
        return None
    else:
        return fileName

def disableEnter(

self)

unbinds from all widgets

def disableEnter(self):
    """ unbinds <Return> from all widgets """
    self.unbindKey("Return")

def disableMenu(

self, title)

def disableMenu(self, title):
    gui.trace('Disabling submenu: %s', title)
    theMenu = self.widgetManager.get(WIDGET_NAMES.Menu, title)
    self._disableMenu(theMenu)

def disableMenuEdit(

self)

def disableMenuEdit(self):
    self.copyAndPaste.inUse = False

def disableMenuItem(

self, title, item)

def disableMenuItem(self, title, item):
    theMenu = self.widgetManager.get(WIDGET_NAMES.Menu, title)
    try:
        gui.trace("Disabling menu item: %s, in menu: %s", item, title)
        self._changeMenuItemState(theMenu, item, DISABLED) 
    except TclError:
        gui.error("Unable to disable menu item: %s, in menu: %s - item not found", item, title)

def disableMenubar(

self)

def disableMenubar(self):
    gui.trace('Disabling toplevel menubar')
    self._disableMenu(self.menuBar)

def disableTableEntry(

self, title, entryPos, disabled=True)

def disableTableEntry(self, title, entryPos, disabled=True):
    grid = self.widgetManager.get(WIDGET_NAMES.Table, title)
    grid.disableEntry(entryPos, disabled=disabled)

def emptyCurrentContainer(

self)

def emptyCurrentContainer(self):
    cConf = self.containerStack[-1]
    kind = WIDGET_NAMES.name(cConf['type'])
    title = cConf['title']
    gui.trace('Emptying current container %s: %s', kind, title)
    if not self._emptyContainerObj(cConf['container']):
        gui.trace('No widgets found in current container %s: %s to empty', kind, title)
    cConf = self._prepContainer(cConf["title"], cConf["type"], cConf["container"], 0, 1)
    self.containerStack[-1] = cConf

def emptySubWindow(

self, title)

def emptySubWindow(self, title):
    # not generated by function generator
    self._emptyContainerType(WIDGET_NAMES.SubWindow, title)

def enableEnter(

self, func, replace=False)

Binds to the specified function - all widgets

def enableEnter(self, func, replace=False):
    """ Binds <Return> to the specified function - all widgets """
    self.bindKey("Return", func, replace)

def enableMenu(

self, title)

def enableMenu(self, title):
    gui.trace('Enabling submenu: %s', title)
    theMenu = self.widgetManager.get(WIDGET_NAMES.Menu, title)
    self._enableMenu(theMenu)

def enableMenuItem(

self, title, item)

def enableMenuItem(self, title, item):
    theMenu = self.widgetManager.get(WIDGET_NAMES.Menu, title)
    try:
        gui.trace("Enabling menu item: %s, in menu: %s", item, title)
        self._changeMenuItemState(theMenu, item, NORMAL) 
    except TclError:
        gui.error("Unable to enable menu item: %s, in menu: %s - item not found", item, title)

def enableMenubar(

self)

def enableMenubar(self):
    gui.trace('Enabling toplevel menubar')
    self._enableMenu(self.menuBar)

def entry(

self, title, value=None, *args, **kwargs)

simpleGUI - adds, sets & gets entries all in one go

def entry(self, title, value=None, *args, **kwargs):
    """ simpleGUI - adds, sets & gets entries all in one go """
    widgKind = WIDGET_NAMES.Entry
    default = kwargs.pop("default", None)
    limit = kwargs.pop("limit", None)
    case = kwargs.pop("case", None)
    rows = kwargs.pop("rows", None)
    secret = kwargs.pop("secret", False)
    kind = kwargs.pop("kind", "standard").lower().strip()
    labBg = kwargs.pop("labBg", None)
    try: self.widgetManager.verify(WIDGET_NAMES.Entry, title)
    except: # widget exists
        if value is not None: self.setEntry(title, value, *args, **kwargs)
        ent = self.getEntry(title)
    else: # new widget
        kwargs = self._parsePos(kwargs.pop("pos", []), kwargs)
        # create the entry widget
        if kind == "auto":
            if value is None: value = []
            ent = self._entryMaker(title, *args, secret=secret, kind=kind, words=value, **kwargs)
        else:
            ent = self._entryMaker(title, *args, secret=secret, kind=kind, **kwargs)
            if not ent: return
    # apply any setter values
    if limit is not None: self.setEntryMaxLength(title, limit)
    if case == "upper": self.setEntryUpperCase(title)
    elif case == "lower": self.setEntryLowerCase(title)
    if default is not None: self.setEntryDefault(title, default)
    if kind != "auto":
        if value is not None: self.setEntry(title, value)
    else:
        if rows is not None: self.setAutoEntryNumRows(title, rows)
    if labBg is not None and self.widgetManager.get(WIDGET_NAMES.Entry, title).isValidation:
        self.setValidationEntryLabelBg(title, labBg)
    # used by file entries
    kwargs.pop("text", None)
    if len(kwargs) > 0:
        self._configWidget(title, widgKind, **kwargs)
    return ent

def error(

message, *args)

wrapper for logMessage - setting level to ERROR

@staticmethod
def error(message, *args):
    """ wrapper for logMessage - setting level to ERROR """
    gui.logMessage(message, "ERROR", *args)

def errorBox(

self, title, message, parent=None)

def errorBox(self, title, message, parent=None):
    self.topLevel.update_idletasks()
    if parent is None:
        MessageBox.showerror(title, message)
        if self.topLevel.displayed:
            self._bringToFront()
    else:
        parent = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent)
        opts = {"parent": parent}
        MessageBox.showerror(title, message, **opts)
        self._bringToFront(parent)

def exception(

message, *args)

wrapper for logMessage - setting level to EXCEPTION

@staticmethod
def exception(message, *args):
    """ wrapper for logMessage - setting level to EXCEPTION """
    gui.logMessage(message, "EXCEPTION", *args)

def exitFullscreen(

self, container=None)

turns off fullscreen mode for the specified window

def exitFullscreen(self, container=None):
    """ turns off fullscreen mode for the specified window """
    if container is None or isinstance(container, UNIVERSAL_STRING):
        try:
            container = self.widgetManager.get(WIDGET_NAMES.SubWindow, container)
        except:
            container = self._getTopLevel()
    if container.isFullscreen:
        container.isFullscreen = False
        container.attributes('-fullscreen', False)
        if container.escapeBindId is not None:
            container.unbind('<Escape>', container.escapeBindId)
        with PauseLogger():
            self._doTitleBar()
        return True
    else:
        return False

def firstFrame(

self, title, callFunction=True)

def firstFrame(self, title, callFunction=True):
    self.widgetManager.get(WIDGET_NAMES.FrameStack, title).showFirstFrame(callFunction)

def floatBox(

self, title, message, parent=None)

def floatBox(self, title, message, parent=None):
    self.topLevel.update_idletasks()
    if parent is None:
        return SimpleDialog.askfloat(title, message)
    else:
        parent = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent)
        opts = {"parent": parent}
        return SimpleDialog.askfloat(title=title, message=message, **opts)

def frame(

*args, **kwds)

@contextmanager
def frame(self, title=None, row=None, column=0, colspan=0, rowspan=0, sticky="NSEW", **kwargs):
    if title is None: # new subFrame
        fr = self.startFrame(title, row, column, colspan, rowspan, sticky)
    else:
        frameNumber = kwargs.pop('frameNumber', None)
        try:
            if frameNumber is not None: fr = self.openSubFrame(title, frameNumber)
            else: fr = self.openFrame(title)
        except: # no widget
            fr = self.startFrame(title, row, column, colspan, rowspan, sticky)
    self.configure(**kwargs)
    try: yield fr
    finally: self.stopFrame()

def frameStack(

*args, **kwds)

@contextmanager
def frameStack(self, title, row=None, column=0, colspan=0, rowspan=0, sticky="NSEW", **kwargs):
    change = kwargs.pop("change", None)
    start = kwargs.pop("start", -1)
    try:
        fr = self.startFrameStack(title, row, column, colspan, rowspan, sticky, change=change, start=start)
    except ItemLookupError:
        fr = self.openFrameStack(title)
    self.configure(**kwargs)
    try: yield fr
    finally:
        self.stopFrameStack()

def frameStackAtEnd(

self, title)

def frameStackAtEnd(self, title):
    return self.widgetManager.get(WIDGET_NAMES.FrameStack, title).atEnd()

def frameStackAtStart(

self, title)

def frameStackAtStart(self, title):
    return self.widgetManager.get(WIDGET_NAMES.FrameStack, title).atStart()

def generateTree(

self, title)

displays data inside tree

def generateTree(self, title):
    """ displays data inside tree """
    tree = self.widgetManager.get(WIDGET_NAMES.Tree, title)
    gui.trace("Generating Tree: %s", title)
    tree.update()
    gui.trace("Tree updated: %s", title)
    tree.expand()
    gui.trace("Tree expanded: %s", title)

def getAllCheckBoxes(

self)

def getAllCheckBoxes(self):
    cbs = {}
    for k in self.widgetManager.group(WIDGET_NAMES.CheckBox):
        cbs[k] = self.getCheckBox(k)
    return cbs

def getAllDatePickers(

self)

def getAllDatePickers(self):
    dps = {}
    for k in self.widgetManager.group(WIDGET_NAMES.DatePicker):
        dps[k] = self.getDatePicker(k)
    return dps

def getAllEntries(

self)

def getAllEntries(self):
    entries = {}
    for k in self.widgetManager.group(WIDGET_NAMES.Entry):
        entries[k] = self.getEntry(k)
    return entries

def getAllInputs(

self, **kwargs)

Get all values, merge & return as a single dictionary.

:param kwargs: will be appended to the input list.

Note, empty pairs from each input is stripped, existing keys will not be overridden!

def getAllInputs(self, **kwargs):
    """Get all values, merge & return as a single dictionary.
    :param kwargs: will be _appended_ to the input list.
    Note, empty pairs from each input is stripped, existing keys
    will not be overridden!
    """
    # used to stop removal of empty inputs
    includeEmptyInputs = kwargs.pop('includeEmptyInputs', False)
    # All available inputs.
    inputs = filter(None, [
              self.getAllEntries(),
              self.getAllOptionBoxes(),
              self.getAllSpinBoxes(),
              self.getAllListBoxes(),
              self.getAllProperties(),
              self.getAllCheckBoxes(),
              self.getAllRadioButtons(),
              self.getAllScales(),
              self.getAllMeters(),
              self.getAllDatePickers(),
              kwargs,
    ])
    result = data = dict()
    for pairs in inputs:
        for key, val in pairs.items():
            # Try and strip values.
            try:
                val = val.strip()
            except AttributeError:
                pass
            try:
                # Skip if value is empty or if key already exists.
                if (not includeEmptyInputs and not val) or result[key]:
                    continue
            except KeyError:
                pass
            result[key] = val
    return result

def getAllListBoxes(

self)

def getAllListBoxes(self):
    boxes = {}
    for k in self.widgetManager.group(WIDGET_NAMES.ListBox):
        boxes[k] = self.getListBox(k)
    return boxes

def getAllListItems(

self, title)

def getAllListItems(self, title):
    lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title)
    items = lb.get(0, END)
    return list(items)

def getAllMeters(

self)

def getAllMeters(self):
    meters = {}
    for k in self.widgetManager.group(WIDGET_NAMES.Meter):
        meters[k] = self.getMeter(k)
    return meters

def getAllOptionBoxes(

self)

Convenience function to get the selected items for all OptionBoxes in the GUI.

:returns: a dictionary containing the result of calling getOptionBox for every OptionBox/TickOptionBox in the GUI

def getAllOptionBoxes(self):
    """ Convenience function to get the selected items for all OptionBoxes in the GUI.
    :returns: a dictionary containing the result of calling getOptionBox for every OptionBox/TickOptionBox in the GUI
    """
    boxes = {}
    for k in self.widgetManager.group(WIDGET_NAMES.OptionBox):
        boxes[k] = self.getOptionBox(k)
    return boxes

def getAllProperties(

self)

def getAllProperties(self):
    props = {}
    for k in self.widgetManager.group(WIDGET_NAMES.Properties):
        props[k] = self.getProperties(k)
    return props

def getAllRadioButtons(

self)

def getAllRadioButtons(self):
    rbs = {}
    for k in self.widgetManager.group(WIDGET_NAMES.RadioButton, group=WidgetManager.VARS):
        rbs[k] = self.getRadioButton(k)
    return rbs

def getAllScales(

self)

def getAllScales(self):
    scales = {}
    for k in self.widgetManager.group(WIDGET_NAMES.Scale):
        scales[k] = self.getScale(k)
    return scales

def getAllSpinBoxes(

self)

def getAllSpinBoxes(self):
    boxes = {}
    for k in self.widgetManager.group(WIDGET_NAMES.SpinBox):
        boxes[k] = self.getSpinBox(k)
    return boxes

def getAllTextAreas(

self)

Convenience function to get the text for all TextAreas in the GUI.

:returns: a dictionary containing the result of calling getTextArea for every TextArea in the GUI

def getAllTextAreas(self):
    """ Convenience function to get the text for all TextAreas in the GUI.
    :returns: a dictionary containing the result of calling getTextArea for every TextArea in the GUI
    """
    areas = {}
    for k in self.widgetManager.group(WIDGET_NAMES.TextArea):
        areas[k] = self.getTextArea(k)
    return areas

def getBg(

self)

def getBg(self):
    if self._getContainerProperty('type') == WIDGET_NAMES.RootPage:
        if not self.ttkFlag:
            return self.bgLabel.cget("bg")
        else:
            return self.bgLabel.cget("background")
    else:
        if not self.ttkFlag:
            return self._getContainerProperty('container').cget("bg")
        else:
            return None

def getButton(

self, name)

def getButton(self, name):
    but = self.widgetManager.get(WIDGET_NAMES.Button, name)
    return but.cget("text")

def getButtonFont(

self)

def getButtonFont(self):
    return self._getContainerProperty('buttonFont').actual()

def getCanvas(

self, title)

def getCanvas(self, title):
    return self.widgetManager.get(WIDGET_NAMES.Canvas, title)

def getCheckBox(

self, title)

def getCheckBox(self, title):
    bVar = self.widgetManager.get(WIDGET_NAMES.CheckBox, title, group=WidgetManager.VARS)
    if bVar.get() == 1:
        return True
    else:
        return False

def getColspan(

self)

def getColspan(self):
    return self.containerStack[-1]['colspan']

def getContainer(

self)

def getContainer(self):
    container = self._getContainerProperty('container')
    if self._getContainerProperty('type') == WIDGET_NAMES.ScrollPane:
        return container.interior
    elif self._getContainerProperty('type') == WIDGET_NAMES.PagedWindow:
        return container.getPage()
    elif self._getContainerProperty('type') == WIDGET_NAMES.ToggleFrame:
        return container.getContainer()
    elif self._getContainerProperty('type') == WIDGET_NAMES.SubWindow:
        return container.canvasPane
    else:
        return container

def getCurrentFrame(

self, title)

def getCurrentFrame(self, title):
    return self.widgetManager.get(WIDGET_NAMES.FrameStack, title).getCurrentFrame()

def getDatePicker(

self, title)

def getDatePicker(self, title):
    self.widgetManager.get(WIDGET_NAMES.DatePicker, title)
    day = int(self.getOptionBox(title + "_DP_DayOptionBox"))
    month = self.MONTH_NAMES.index(
        self.getOptionBox(
            title + "_DP_MonthOptionBox")) + 1
    year = int(self.getOptionBox(title + "_DP_YearOptionBox"))
    date = datetime.date(year, month, day)
    return date

def getEntry(

self, name)

def getEntry(self, name):
    entry = self.widgetManager.get(WIDGET_NAMES.Entry, name)
    if entry.showingDefault:
        if entry.isNumeric:
            return None
        else:
            return ""
    else:
        val = self.widgetManager.get(WIDGET_NAMES.Entry, name, group=WidgetManager.VARS).get()
        if entry.isNumeric:
            if len(val) == 0 or (len(val) == 1 and val in '.-') or (len(val) == 2 and val == "-."):
                return None
            else:
                return float(val)
        else:
            return val

def getExpand(

self)

def getExpand(self):
    return self._getContainerProperty('expand')

def getFastStop(

self)

def getFastStop(self):
    return self._fastStop

def getFg(

self)

def getFg(self):
    return self._getContainerProperty("fg")

def getFocus(

self)

def getFocus(self):
    widg = self.topLevel.focus_get()
    return self.widgetManager.getName(widg)

def getFont(

self)

def getFont(self):
    return self._getContainerProperty('labelFont').actual()

def getFonts(

self)

def getFonts(self):
    fonts = list(tkFont.families())
    fonts.sort()
    return fonts

def getFullscreen(

self, title=None)

def getFullscreen(self, title=None):
    if title is None:
        container = self._getTopLevel()
    else:
        container = self.widgetManager.get(WIDGET_NAMES.SubWindow, title)
    return container.isFullscreen

def getGoogleMapLocation(

self, title)

def getGoogleMapLocation(self, title):
    return self.widgetManager.get(WIDGET_NAMES.Map, title).params["center"]

def getGoogleMapSize(

self, title)

def getGoogleMapSize(self, title):
    return self.widgetManager.get(WIDGET_NAMES.Map, title).params["size"]

def getGoogleMapTerrain(

self, title)

def getGoogleMapTerrain(self, title):
    return self.widgetManager.get(WIDGET_NAMES.Map, title).params["maptype"].title()

def getGoogleMapZoom(

self, title)

def getGoogleMapZoom(self, title):
    return self.widgetManager.get(WIDGET_NAMES.Map, title).params["zoom"]

def getGridEntries(

self, title)

def getGridEntries(self, title):
    gui.warn("Deprecated - grids renamed to tables")
    return self.getTableEntries(title)

def getGridRow(

self, title, rowNumber)

def getGridRow(self, title, rowNumber):
    return self.getTableRow(title, rowNumber)

def getGridRowCount(

self, title)

def getGridRowCount(self, title):
    return self.getTableRowCount(title)

def getGridSelectedCells(

self, title)

def getGridSelectedCells(self, title):
    gui.warn("Deprecated - grids renamed to tables")
    return self.getTableSelectedCells(title)

def getGuiPadding(

self)

def getGuiPadding(self):
    return int(str(self.containerStack[0]['container'].cget('padx'))), int(str(self.containerStack[0]['container'].cget('pady')))

def getIcon(

self)

def getIcon(self):
    container = self._getTopLevel()
    return container.winIcon

def getImage(

self, name)

def getImage(self, name):
    label = self.widgetManager.get(WIDGET_NAMES.Image, name)
    return label.image.path

def getImageDimensions(

self, name)

def getImageDimensions(self, name):
    img = self.widgetManager.get(WIDGET_NAMES.Image, name).image
    return img.width(), img.height()

def getImagePath(

self, imagePath)

def getImagePath(self, imagePath):
    if imagePath is None:
        return None
    if self.userImages is not None:
        imagePath = os.path.join(self.userImages, imagePath)
    absPath = os.path.abspath(imagePath)
    return absPath

def getInPadding(

self)

def getInPadding(self):
    return self._getContainerProperty('ipadx'), self._getContainerProperty('ipady')

def getInputFont(

self)

def getInputFont(self):
    return self._getContainerProperty('inputFont').actual()

def getLabel(

self, name)

def getLabel(self, name):
    lab = self.widgetManager.get(WIDGET_NAMES.Label, name)
    return lab.cget("text")

def getLabelFont(

self)

def getLabelFont(self):
    return self._getContainerProperty('labelFont').actual()

def getLanguage(

self)

returns the current language

def getLanguage(self):
    ''' returns the current language '''
    return self._language

def getListBox(

self, title)

def getListBox(self, title):
    lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title)
    items = lb.curselection()
    values = []
    for loop in range(len(items)):
        values.append(lb.get(items[loop]))
    return values

def getListBoxPos(

self, title)

def getListBoxPos(self, title):
    lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title)
    # bug in tkinter 1.160 returns these as strings
    items = [int(i) for i in lb.curselection()]
    return items

def getLocation(

self)

def getLocation(self):
    container = self._getTopLevel()
    size, loc = gui.SPLIT_GEOM(container.geometry())
    return loc

def getLogFile(

self)

def getLogFile(self):
    return logging.root.handlers[0].baseFilename

def getLogLevel(

self)

def getLogLevel(self):
    return logging.getLevelName(logging.getLogger("appJar").getEffectiveLevel())

def getMenuCheckBox(

self, menu, title)

def getMenuCheckBox(self, menu, title):
    return self._getMenu(menu, title, "cb")

def getMenuRadioButton(

self, menu, title)

def getMenuRadioButton(self, menu, title):
    return self._getMenu(menu, title, "rb")

def getMessage(

self, title)

def getMessage(self, title):
    mess = self.widgetManager.get(WIDGET_NAMES.Message, title)
    return mess.cget("text")

def getMeter(

self, name)

def getMeter(self, name):
    item = self.widgetManager.get(WIDGET_NAMES.Meter, name)
    return item.get()

def getOnTop(

self)

def getOnTop(self):
    return self._getTopLevel().attributes("-topmost") == 1

def getOptionBox(

self, title)

Gets the selected item from the named OptionBox

:param title: the OptionBox to check :returns: the selected item in an OptionBox or a dictionary of all items and their status for a TickOptionBox :raises ItemLookupError: if the title can't be found

def getOptionBox(self, title):
    """ Gets the selected item from the named OptionBox
    :param title: the OptionBox to check
    :returns: the selected item in an OptionBox or a dictionary of all items and their status for a TickOptionBox
    :raises ItemLookupError: if the title can't be found
    """
    box = self.widgetManager.get(WIDGET_NAMES.OptionBox, title)
    if box.kind == "ticks":
        val = self.widgetManager.get(WIDGET_NAMES.TickOptionBox, title, group=WidgetManager.VARS)
        retVal = {}
        for k, v in val.items():
            retVal[k] = bool(v.get())
        return retVal
    else:
        val = self.widgetManager.get(WIDGET_NAMES.OptionBox, title, group=WidgetManager.VARS)
        val = val.get().strip()
        # set to None if it's a divider
        if val.startswith("-") or len(val) == 0:
            val = None
        return val

def getPadding(

self)

def getPadding(self):
    return self._getContainerProperty('padx'), self._getContainerProperty('pady')

def getPagedWindowPageNumber(

self, title)

def getPagedWindowPageNumber(self, title):
    pager = self.widgetManager.get(WIDGET_NAMES.PagedWindow, title)
    return pager.getPageNumber()

def getPopUp(

self)

def getPopUp(self):
    return self.topLevel.POP_UP

def getPreviousFrame(

self, title)

def getPreviousFrame(self, title):
    return self.widgetManager.get(WIDGET_NAMES.FrameStack, title).getPreviousFrame()

def getProperties(

self, title)

def getProperties(self, title):
    props = self.widgetManager.get(WIDGET_NAMES.Properties, title)
    return props.getProperties()

def getProperty(

self, title, prop)

def getProperty(self, title, prop):
    props = self.widgetManager.get(WIDGET_NAMES.Properties, title)
    return props.getProperty(prop)

def getRadioButton(

self, title)

def getRadioButton(self, title):
    var = self.widgetManager.get(WIDGET_NAMES.RadioButton, title, group=WidgetManager.VARS)
    return var.get()

def getRandomColour(

self)

generates a random colour

def getRandomColour(self):
    """ generates a random colour """
    self._loadRandom()
    de=("%02x"%random.randint(0,255))
    re=("%02x"%random.randint(0,255))
    we=("%02x"%random.randint(0,255))
    return "#"+de+re+we

def getResizable(

self)

def getResizable(self):
    return self._getTopLevel().isResizable

def getRow(

self)

def getRow(self):
    return self._getContainerProperty('emptyRow')

def getRowspan(

self)

def getRowspan(self):
    return self.containerStack[-1]['rowspan']

def getScale(

self, title)

def getScale(self, title):
    sc = self.widgetManager.get(WIDGET_NAMES.Scale, title)
    return sc.get()

def getSetting(

self, name, default=None)

gets a setting form the settings file

def getSetting(self, name, default=None):
    """ gets a setting form the settings file """
    try: return self.externalSettings[name]
    except: return default

def getSize(

self)

def getSize(self):
    container = self._getTopLevel()
    size, loc = gui.SPLIT_GEOM(container.geometry())
    return size

def getSpinBox(

self, title)

def getSpinBox(self, title):
    spin = self.widgetManager.get(WIDGET_NAMES.SpinBox, title)
    return spin.get()

def getStatusFont(

self)

def getStatusFont(self):
    return self._statusFont.actual()

def getSticky(

self)

def getSticky(self):
    return self._getContainerProperty('sticky')

def getStretch(

self)

def getStretch(self):
    return self.getExpand()

def getTabbedFrameSelectedTab(

self, title)

def getTabbedFrameSelectedTab(self, title):
    nb = self.widgetManager.get(WIDGET_NAMES.TabbedFrame, title)
    return nb.getSelectedTab()

def getTableEntries(

self, title)

def getTableEntries(self, title):
    return self.widgetManager.get(WIDGET_NAMES.Table, title).getEntries()

def getTableLastChange(

self, title)

def getTableLastChange(self, title):
    return self.widgetManager.get(WIDGET_NAMES.Table, title).getLastChange()

def getTableRow(

self, title, rowNumber)

def getTableRow(self, title, rowNumber):
    grid = self.widgetManager.get(WIDGET_NAMES.Table, title)
    return grid.getRow(rowNumber)

def getTableRowCount(

self, title)

def getTableRowCount(self, title):
    grid = self.widgetManager.get(WIDGET_NAMES.Table, title)
    return grid.getRowCount()

def getTableSelectedCells(

self, title)

def getTableSelectedCells(self, title):
    return self.widgetManager.get(WIDGET_NAMES.Table, title).getSelectedCells()

def getTextArea(

self, title)

Gets the text in the specified TextArea

:param title: the TextArea to check :returns: the text in the specified TextArea :raises ItemLookupError: if the title can't be found

def getTextArea(self, title):
    """ Gets the text in the specified TextArea
    :param title: the TextArea to check
    :returns: the text in the specified TextArea
    :raises ItemLookupError: if the title can't be found
    """
    return self.widgetManager.get(WIDGET_NAMES.TextArea, title).getText()

def getTextAreaTag(

self, title, tag)

returns all details about the specified tag

def getTextAreaTag(self, title, tag):
    """ returns all details about the specified tag """
    ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title)
    return ta.tag_config(tag)

def getTextAreaTags(

self, title)

returns a list of all tags in the text area

def getTextAreaTags(self, title):
    """ returns a list of all tags in the text area """
    ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title)
    return ta.tag_names()

def getTitle(

self)

def getTitle(self):
    return self._getTopLevel().title()

def getToggleFrameState(

self, title)

def getToggleFrameState(self, title):
    toggle = self.widgetManager.get(WIDGET_NAMES.ToggleFrame, title)
    return toggle.isShowing()

def getTransparency(

self)

def getTransparency(self):
    return self._getTopLevel().attributes("-alpha") * 100

def getTreeSelected(

self, title)

def getTreeSelected(self, title):
    tree = self.widgetManager.get(WIDGET_NAMES.Tree, title)
    return tree.getSelectedText()

def getTreeSelectedXML(

self, title)

def getTreeSelectedXML(self, title):
    tree = self.widgetManager.get(WIDGET_NAMES.Tree, title)
    item = tree.getSelected()
    if item is not None:
        return item.node.toxml()
    else:
        return None

def getTreeXML(

self, title)

def getTreeXML(self, title):
    tree = self.widgetManager.get(WIDGET_NAMES.Tree, title)
    return tree.item.node.toxml()

def getTtkTheme(

self)

def getTtkTheme(self):
    return self.ttkStyle.theme_use()

def getTtkThemes(

self, loadThemes=False)

def getTtkThemes(self, loadThemes=False):
    if loadThemes:
        self._loadTtkThemes()
        if not ThemedStyle:
            self.error("Custom ttkThemes not available")
    return self.ttkStyle.theme_names()

def getTurtle(

self, title)

def getTurtle(self, title):
    return self.widgetManager.get(WIDGET_NAMES.Turtle, title).turtle

def getTurtleScreen(

self, title)

def getTurtleScreen(self, title):
    return self.widgetManager.get(WIDGET_NAMES.Turtle, title).screen

def getVisible(

self)

def getVisible(self):
    return self.topLevel.displayed

def getWidget(

self, kind, name, val=None)

def getWidget(self, kind, name, val=None):
    # if val is set (RadioButtons) - append it
    if val is not None: name+= "-" + val
    return self.widgetManager.get(kind, name)

def getWidgetProperty(

self, kind, name, val, prop)

def getWidgetProperty(self, kind, name, val, prop):
    return self.getWidget(kind, name, val).cget(prop)

def go(

self, language=None, startWindow=None)

Most important function! starts the GUI

def go(self, language=None, startWindow=None):
    """ Most important function! starts the GUI """
    # check if we have a command line language
    if self._language is not None:
        language = self._language
    # if language is populated, we are in internationalisation mode
    # call the changeLanguage function - to re-badge all the widgets
    if language is not None:
        self.changeLanguage(language)
    if self.splashConfig is not None:
        gui.trace("SPLASH: %s", self.splashConfig)
        splash = SplashScreen(
                        self.topLevel,
                        self.splashConfig['text'],
                        self.splashConfig['fill'],
                        self.splashConfig['stripe'],
                        self.splashConfig['fg'],
                        self.splashConfig['font']
                        )
        self.topLevel.withdraw()
        self._bringToFront(splash)
    # check the containers have all been stopped
    if len(self.containerStack) > 1:
        for i in range(len(self.containerStack) - 1, 0, -1):
            kind = self.containerStack[i]['type']
            if kind != WIDGET_NAMES.Pane:
                self.warn("No stopContainer called on: %s", WIDGET_NAMES.name(kind))
    # update any trees
    for k in self.widgetManager.group(WIDGET_NAMES.Tree):
        self.generateTree(k)
    # create appJar menu, if no menuBar created
    if not self.hasMenu:
        self.addAppJarMenu()
    if self.platform == self.WINDOWS:
        self.menuBar.add_cascade(menu=self.widgetManager.get(WIDGET_NAMES.Menu, "WIN_SYS"))
    self.topLevel.config(menu=self.menuBar)
    if startWindow is not None:
        self.startWindow = startWindow
        gui.trace("startWindow parameter: %s", startWindow)
    # pack it all in & make sure it's drawn
    self.appWindow.pack(fill=BOTH)
    if self.useSettings:
        self.loadSettings(self.settingsFile)
    self.topLevel.update_idletasks()
    # check geom is set and set a minimum size, also positions the window if necessary
    if not self.topLevel.locationSet:
        self.setLocation('CENTER')
    if not hasattr(self.topLevel, 'ms'):
        self.setMinSize()
    if self.splashConfig is not None:
        time.sleep(3)
        splash.destroy()
    # user hasn't specified anything
    if self.startWindow is None:
        if not self.topLevel.displayed:
            gui.trace("topLevel has been manually hidden - not showing in go()")
        else:
            gui.trace("Showing topLevel")
            self._bringToFront()
            self.topLevel.deiconify()
    else:
        gui.trace("hiding main window")
        self.hide()
        sw = self.widgetManager.get(WIDGET_NAMES.SubWindow, startWindow)
        if sw.blocking:
            raise Exception("Unable to start appjar with a blocking subWindow")
        self.showSubWindow(startWindow)
    # required to make the gui reopen after minimising
    if self.GET_PLATFORM() == self.MAC:self.topLevel.createcommand('tk::mac::ReopenApplication', self._macReveal)
    # start the call back & flash loops
    self._poll()
    self._flash()
    # register start-up function
    if self.topLevel.startFunction is not None:
        self.topLevel.after_idle(self.topLevel.startFunction)
    # start the main loop
    try:
        self.topLevel.mainloop()
    except(KeyboardInterrupt, SystemExit) as e:
        gui.trace("appJar stopped through ^c or exit()")
        self.stop()
    except Exception as e:
        self.exception(e)
        self.stop()

def gr(

self)

def gr(self):
    return self.getRow()

def grip(

self, *args, **kwargs)

simpleGUI - adds grip

def grip(self, *args, **kwargs):
    """ simpleGUI - adds grip """
    kwargs = self._parsePos(kwargs.pop("pos", []), kwargs)
    return self.addGrip(*args, **kwargs)

def growImage(

self, name, x, y='')

def growImage(self, name, x, y=''):
    label = self.widgetManager.get(WIDGET_NAMES.Image, name)
    image = label.image.zoom(x, y)
    label.config(image=image)
    label.config(anchor=CENTER, font=self._getContainerProperty('labelFont'))
    if not self.ttkFlag:
        label.config(background=self._getContainerBg())
        label.config(width=image.width(), height=image.height())
    label.modImage = image  # keep a reference!

def hasImageChanged(

self, originalImage, newImage)

def hasImageChanged(self, originalImage, newImage):
    newAbsImage = self.getImagePath(newImage)
    if originalImage is None:
        return True
    # filename has changed
    if originalImage.path != newAbsImage:
        return True
    # modification time has changed
    if originalImage.modTime != os.path.getmtime(newAbsImage):
        return True
    # no changes
    return False

def hide(

self, btn=None)

def hide(self, btn=None):
    self._getTopLevel().displayed = False
    self._getTopLevel().withdraw()

def hideAllSubWindows(

self, useStopFunction=False)

def hideAllSubWindows(self, useStopFunction=False):
    for sub in self.widgetManager.group(WIDGET_NAMES.SubWindow):
        self.hideSubWindow(sub, useStopFunction)

def hideSubWindow(

self, title, useStopFunction=False)

def hideSubWindow(self, title, useStopFunction=False):
    self.widgetManager.get(WIDGET_NAMES.SubWindow, title).hide(useStopFunction)

def hideTabbedFrameTab(

self, title, tab)

def hideTabbedFrameTab(self, title, tab):
    nb = self.widgetManager.get(WIDGET_NAMES.TabbedFrame, title)
    nb.hideTab(tab)

def hideTitleBar(

self)

def hideTitleBar(self):
    self.hasTitleBar = False
    self._doTitleBar()

def hideToolbar(

self)

def hideToolbar(self):
    self.tb.hide()

def hideWidgetType(

self, kind, name, collapse=False)

def hideWidgetType(self, kind, name, collapse=False):
    items = self._getWidgetList(kind, name, limit=False)
    for item in items:
        if self._widgetHasContainer(kind, item):
            gui.trace("Hiding widget in container: %s", name)
            widget = item.master
            if hasattr(widget, "inContainer") and widget.inContainer:
                gui.trace("Have container in container")
                widget = widget.master
            try: self.widgetManager.get(WIDGET_NAMES.FrameLabel, name).hidden = True
            except: pass
        else:
            gui.trace("Hiding widget: %s", name)
             if kind in [WIDGET_NAMES.RadioButton]:
                 for rb in item:
                     if rb.text == name:
                         widget = rb
            widget = item
        if "in" in widget.grid_info():
            gui.trace("Widget hidden: %s", name)
            info = widget.grid_info()
            widget.grid_remove()
            if collapse:
                widget.master.grid_rowconfigure(info["row"], minsize=0, weight=0)
        else:
            gui.trace("Hiding failed - %s not showing", name)

def highlightTextArea(

self, title, start, end='end')

selects text in the specified range

def highlightTextArea(self, title, start, end=END):
    """ selects text in the specified range """
    ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title)
    ta.tag_add(SEL, start, end)

def image(

self, title, value=None, *args, **kwargs)

simpleGUI - adds, sets & gets images all in one go

def image(self, title, value=None, *args, **kwargs):
    """ simpleGUI - adds, sets & gets images all in one go """
    widgKind = WIDGET_NAMES.Image
    kind = kwargs.pop("kind", "standard").lower().strip()
    speed = kwargs.pop("speed", None)
    drop = kwargs.pop("drop", None)
    over = kwargs.pop("over", None)
    submit = kwargs.pop("submit", None)
    _map = kwargs.pop("map", None)
    try: self.widgetManager.verify(widgKind, title)
    except: # already exists
        if value is not None:
            if kind == "data":
                self.setImageData(title, value, **kwargs)
            elif kind == "icon":
                gui.warn("Changing image icons not yet supported: %s.", title)
            else:
                self.setImage(title, value)
        image =  self.getImage(title)
    else: # new widget
        kwargs = self._parsePos(kwargs.pop("pos", []), kwargs)
        if kind == "icon":
            image = self.addIcon(title, value, *args, **kwargs)
        elif kind == "data":
            image = self.addImageData(title, value, *args, **kwargs)
        else:
            image = self.addImage(title, value, *args, **kwargs)
    if speed is not None: self.setAnimationSpeed(title, speed)
    if over is not None: self.setImageMouseOver(title, over)
    if submit is not None:
        if _map is not None: self.setImageMap(title, submit, _map)
        else: self.setImageSubmitFunction(title, submit)
    elif submit is None and _map is not None:
        gui.warn("Must specify a submit function when setting an image map: %s", title)
    if drop is not None: self.setImageDropTarget(title, drop)
    if len(kwargs) > 0:
        self._configWidget(title, widgKind, **kwargs)
    return image

def increaseButtonFont(

self)

def increaseButtonFont(self):
    self.setButtonFont(size=self._buttonFont['size'] + 1)

def increaseFont(

self)

def increaseFont(self):
    self.increaseLabelFont()
    self.increaseButtonFont()

def increaseLabelFont(

self)

def increaseLabelFont(self):
    self.setLabelFont(size=self._labelFont['size'] + 1)

def info(

message, *args)

wrapper for logMessage - setting level to INFO

@staticmethod
def info(message, *args):
    """ wrapper for logMessage - setting level to INFO """
    gui.logMessage(message, "INFO", *args)

def infoBox(

self, title, message, parent=None)

def infoBox(self, title, message, parent=None):
    self.topLevel.update_idletasks()
    if parent is None:
        MessageBox.showinfo(title, message)
        if self.topLevel.displayed:
            self._bringToFront()
    else:
        parent = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent)
        opts = {"parent": parent}
        MessageBox.showinfo(title, message, **opts)
        self._bringToFront(parent)

def integerBox(

self, title, message, parent=None)

def integerBox(self, title, message, parent=None):
    self.topLevel.update_idletasks()
    if parent is None:
        return SimpleDialog.askinteger(title, message)
    else:
        parent = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent)
        opts = {"parent": parent}
        return SimpleDialog.askinteger(title=title, message=message, **opts)

def label(

self, title, value=None, *args, **kwargs)

simpleGUI - adds, sets & gets labels all in one go

def label(self, title, value=None, *args, **kwargs):
    """ simpleGUI - adds, sets & gets labels all in one go """
    widgKind = WIDGET_NAMES.Label
    kind = kwargs.pop("kind", "standard").lower().strip()
    try: self.widgetManager.verify(widgKind, title)
    except: # widget exists
        if value is not None: self.setLabel(title, value)
        label = self.getLabel(title)
    else: # new widget
        kwargs = self._parsePos(kwargs.pop("pos", []), kwargs)
        if kind == "flash": label = self._labelMaker(title, value, kind, *args, **kwargs)
        elif kind == "selectable": label = self._labelMaker(title, value, kind, *args, **kwargs)
        else: label = self._labelMaker(title, value, "label", *args, **kwargs)
    if len(kwargs) > 0:
        self._configWidget(title, widgKind, **kwargs)
    return label

def labelFrame(

*args, **kwds)

@contextmanager
def labelFrame(self, title, row=None, column=0, colspan=0, rowspan=0, sticky=W, hideTitle=False, **kwargs):
    name = kwargs.pop("label", kwargs.pop("name", None))
    labelFg = kwargs.pop("labelFg", self.fg)
    try:
        lf = self.startLabelFrame(title, row, column, colspan, rowspan, sticky, hideTitle, name)
    except ItemLookupError:
        lf = self.openLabelFrame(title)
    self.configure(**kwargs)
    if not self.ttkFlag:
        lf.config(fg=labelFg)
    try: yield lf
    finally: self.stopLabelFrame()

def lastFrame(

self, title, callFunction=True)

def lastFrame(self, title, callFunction=True):
    self.widgetManager.get(WIDGET_NAMES.FrameStack, title).showLastFrame(callFunction)

simpleGUI - adds, sets & gets links all in one go

def listBox(

self, title, value=None, *args, **kwargs)

simpleGUI -- adds, sets & gets listBoxes all in one go

def listBox(self, title, value=None, *args, **kwargs):
    """ simpleGUI -- adds, sets & gets listBoxes all in one go """
    widgKind = WIDGET_NAMES.ListBox
    rows = kwargs.pop("rows", None)
    multi = kwargs.pop("multi", False)
    group = kwargs.pop("group", False)
    selected = kwargs.pop("selected", None)
    first = kwargs.pop("first", False)
    callFunction = kwargs.pop("callFunction", True)
    # select=select, deselect=??, toggle=??, clear=??, rename=set, replace=update, delete=remove
    if value is None: mode = 'get'
    else: mode = 'select'
    mode = kwargs.pop("mode", mode)
    try: self.widgetManager.verify(widgKind, title)
    except: # widget exists
        if mode == "select":
            if value is not None:
                if isinstance(value, int):
                    self.selectListItemAtPos(title, value, *args, **kwargs)
                else:
                    self.selectListItem(title, value, *args, **kwargs)
            else: gui.error("No item specified to select in listbox: %s", title)
        elif mode == "deselect":
            if value is not None:
                if isinstance(value, int):
                    self.deselectListItemAtPos(title, value, *args, **kwargs)
                else:
                    self.deselectListItem(title, value, *args, **kwargs)
            else: gui.error("No item specified to deselect in listbox: %s", title)
        elif mode == "toggle":
            gui.error("%s not implemented yet in listbox: %s", mode, title)
        elif mode == "clear":
            self.deselectAllListItems(title)
        elif mode == "rename":
            gui.error("%s not implemented yet in listbox: %s", mode, title)
        elif mode == "replace":
            if value is not None: self.updateListBox(title, items=value, callFunction=callFunction)
            else: gui.error("No values specified to replace in listbox: %s", title)
        elif mode == "delete":
            if value is not None:
                if isinstance(value, int):
                    self.removeListItemAtPos(title, value)
                else:
                    self.removeListItem(title, value)
            else: gui.error("No value specified to delete in listbox: %s", title)
        elif mode == "add":
            if value is not None:
                select = True if selected is None else selected
                if type(value) in (list, tuple):
                    self.addListItems(title, items=value, select=select)
                else:
                    self.addListItem(title, item=value, select=select)
            else: gui.error("No value specified to add in listbox: %s", title)
        elif mode == "get":
            pass
        else:
            gui.error("Invalid mode (%s) specified in listbox: %s", mode, title)
        listBox = self.getListBox(title)
    else: # new widget
        kwargs = self._parsePos(kwargs.pop("pos", []), kwargs)
        listBox = self._listBoxMaker(title, value, *args, **kwargs)
    if rows is not None: self.setListBoxRows(title, rows)
    if multi: self.setListBoxMulti(title)
    if group: self.setListBoxGroup(title)
    if selected is not None: self.selectListItemAtPos(title, selected, callFunction=False)
    if len(kwargs) > 0:
        self._configWidget(title, widgKind, **kwargs)
    return listBox

def listbox(

self, title, value=None, *args, **kwargs)

simpleGUI - shortner for listBox()

def listbox(self, title, value=None, *args, **kwargs):
    """ simpleGUI - shortner for listBox() """
    return self.listBox(title, value, *args, **kwargs)

def loadSettings(

self, fileName='appJar.ini', useSettings=True)

loads setting from a settings file, and adjusts the GUI to match called by go() function, if user has requested settings

def loadSettings(self, fileName="appJar.ini", useSettings=True):
    """ loads setting from a settings file, and adjusts the GUI to match
        called by go() function, if user has requested settings """
    self._loadConfigParser()
    if not ConfigParser:
        self.error("Unable to save config file - no configparser")
        return
    self.useSettings = useSettings
    settings = ConfigParser()
    settings.optionxform = str
    settings.read(fileName)
    if settings.has_option("GEOM", "geometry"):
        geom = settings.get("GEOM", "geometry")
        if not self.topLevel.ignoreSettings:
            size, loc = gui.SPLIT_GEOM(geom)
            gui.trace("Setting topLevel geom: %s as size: %s, loc: %s", geom, size, loc)
            if size[0] > 1:
                self.setSize(*size)
            if loc[0] != -1:
                self.setLocation(*loc)
        else:
            gui.trace("Ignoring topLevel geom: %s", geom)
    # not finished
    if settings.has_option("GEOM", "fullscreen"):
        fs = settings.getboolean('GEOM', "fullscreen")
        gui.trace("Set fullscreen to: %s", fs)
        if fs: self.setFullscreen()
        else: self.exitFullscreen()
    if settings.has_option("GEOM", "minsize"):
        self.topLevel.ms = settings.get('GEOM', "minsize").split(",")
        self._getTopLevel().minsize(self.topLevel.ms[0], self.topLevel.ms[1])
        gui.trace("Set minsize to: %s", self.topLevel.ms)
    if settings.has_option("GEOM", "state"):
        state = settings.get('GEOM', "state")
        if state in ["withdrawn", "zoomed"]:
            self._getTopLevel().state(state)
    if settings.has_option("TOOLBAR", "pinned") and self.tb.inUse:
        tb = settings.getboolean("TOOLBAR", "pinned")
        self.setToolbarPinned(tb)
        gui.trace("Set toolbar to: %s", tb)
    if "TOGGLES" in settings.sections():
        for k in settings.options("TOGGLES"):
            try:
                if self.getToggleFrameState(k) != settings.getboolean("TOGGLES", k):
                    self.toggleToggleFrame(k)
            except ItemLookupError:
                gui.error("Settings error, invalid TOGGLES name: %s - discarding", k)
    if "TABS" in settings.sections():
        for k in settings.options("TABS"):
            try:
                self.setTabbedFrameSelectedTab(k, settings.get("TABS", k))
            except ItemLookupError:
                gui.error("Settings error, invalid TABS name: %s - discarding", k)
    if "PAGES" in settings.sections():
        for k in settings.options("PAGES"):
            try:
                self.setPagedWindowPage(k, settings.getint("PAGES", k))
            except ItemLookupError:
                gui.error("Settings error, invalid PAGES name: %s - discarding", k)
    if "SUBWINDOWS" in settings.sections():
        for k in settings.options("SUBWINDOWS"):
            if settings.getboolean("SUBWINDOWS", k):
                gui.trace("Loading settings for %s", k)
                try:
                    tl = self.widgetManager.get(WIDGET_NAMES.SubWindow, k)
                    # process the geom settings
                    if settings.has_option(k, "geometry"):
                        geom = settings.get(k, "geometry")
                        size, loc = gui.SPLIT_GEOM(geom)
                        if size[0] > 1:
                            gui.trace("Setting size: %s", size)
                            tl.geometry("%sx%s" % (size[0], size[1]))
                            tl.shown = True
                        else:
                            gui.trace("Skipping size: %s", size)
                        if loc[0] > -1:
                            gui.trace("Setting location: %s", loc)
                            self.setSubWindowLocation(k, *loc)
                        else:
                            gui.trace("Skipping location: %s", loc)
                    else:
                        gui.trace("No location found")
                    if settings.has_option(k, "minsize"):
                        ms = settings.get(k, "minsize").split(",")
                        self.setMinSize(tl, ms)
                    # set the state - if there' no startWindow
                    if self.startWindow is None:
                        try:
                            tl.state(settings.get(k, "state"))
                            gui.trace("Set state=%s", tl.state())
                        except: pass # no state found
                except ItemLookupError:
                    gui.error("Settings error, invalid SUBWINDOWS name: %s - discarding.", k)
            else:
                gui.trace("Skipping settings for %s", k)
    if "EXTERNAL" in settings.sections():
        for k in settings.options("EXTERNAL"):
            self.externalSettings[k] = settings.get("EXTERNAL", k)

def logMessage(

msg, level, *args)

allows user to log a message - provide a message and a log level any %s tags in the message will be replaced by the relevant positional *args

@staticmethod
def logMessage(msg, level, *args):
    """ allows user to log a message - provide a message and a log level
        any %s tags in the message will be replaced by the relevant positional *args """
    frames = inspect.stack()
    # try to ensure we only log extras if we're called from above functions
    if frames[1][3] in ("exception", "critical", "error", "warn", "debug", "trace", "info"):
        callFrame = ""
        try:
            progName = gui.exe_file
            for s in frames:
                if progName in s[1]:
                    callFrame = s
                    break
        except: pass
        if callFrame != "":
            callFrame = "Line " + str(callFrame[2])
        # user generated call
        if "appjar.py" not in frames[2][1] or frames[2][3] == "handlerFunction":
            if callFrame != "":
                msg = "[" + callFrame + "]: "+str(msg)
        # appJar logging
        else:
            if callFrame != "":
                msg = "["+callFrame + "->" + str(frames[2][2]) +"/"+str(frames[2][3])+"]: "+str(msg)
            else:
                msg = "["+str(frames[2][2]) +"/"+str(frames[2][3])+"]: "+str(msg)
    logger = logging.getLogger("appJar")
    level = level.upper()
    if level == "EXCEPTION": logger.exception(msg, *args)
    elif level == "CRITICAL": logger.critical(msg, *args)
    elif level == "ERROR": logger.error(msg, *args)
    elif level == "WARNING": logger.warning(msg, *args)
    elif level == "INFO": logger.info(msg, *args)
    elif level == "DEBUG": logger.debug(msg, *args)
    elif level == "TRACE": logger.trace(msg, *args)

def logTextArea(

self, title)

Creates an md5 hash - can be used later to check if the TextArea has changed The hash is stored in the widget

:param title: the TextArea to hash :returns: None :raises ItemLookupError: if the title can't be found

def logTextArea(self, title):
    """ Creates an md5 hash - can be used later to check if the TextArea has changed
    The hash is stored in the widget
    :param title: the TextArea to hash
    :returns: None
    :raises ItemLookupError: if the title can't be found
    """
    self._loadHashlib()
    if hashlib is False:
        self.warn("Unable to log TextArea, hashlib library not available")
    else:
        text = self.widgetManager.get(WIDGET_NAMES.TextArea, title)
        text.__hash = text.getTextAreaHash()

def loopSound(

self, sound)

def loopSound(self, sound):
    self._soundWrap(sound, True, True)

def makeListBoxContainer(

self)

def makeListBoxContainer(self):
    ParentBox = self._makeParentBox()
    class ListBoxContainer(Frame, object):
        def __init__(self, parent, **opts):
            super(ListBoxContainer, self).__init__(parent)
        # customised config setters
        def config(self, cnf=None, **kw):
            self.configure(cnf, **kw)
        def configure(self, cnf=None, **kw):
            # properties to propagate to CheckBoxes
            kw = gui.CLEAN_CONFIG_DICTIONARY(**kw)
            # propagate anything left
            super(ListBoxContainer, self).config(cnf, **kw)
    return ListBoxContainer

def map(

self, title, value=None, *args, **kwargs)

simpleGUI - adds, sets & gets maps all in one go

def map(self, title, value=None, *args, **kwargs):
    """ simpleGUI - adds, sets & gets maps all in one go """
    widgKind = WIDGET_NAMES.Map
    zoom = kwargs.pop("zoom", None)
    size = kwargs.pop("size", None)
    terrain = kwargs.pop("terrain", None)
    proxy = kwargs.pop("proxy", None)
    try: self.widgetManager.verify(widgKind, title)
    except: # widget exists
        gMap = self.getLabel(title)
    else: # new widget
        kwargs = self._parsePos(kwargs.pop("pos", []), kwargs)
        gMap = self.addGoogleMap(title, *args, **kwargs)
    if value is not None: self.setGoogleMapLocation(title, value)
    if zoom is not None: self.setGoogleMapZoom(title, zoom)
    if size is not None: self.setGoogleMapSize(title, size)
    if terrain is not None: self.setGoogleMapTerrain(title, terrain)
    if proxy is not None: self.setGoogleMapProxy(title, proxy)
    if len(kwargs) > 0:
        self._configWidget(title, widgKind, **kwargs)
    return gMap

def menu(

self, menu, name=None, func=None, **kwargs)

def menu(self, menu, name=None, func=None, **kwargs):
    # kind: menu, sub, button, sep, check/tick, radio
    kind = kwargs.pop('kind', 'button')
    group = kwargs.pop('group', None)
    shortcut = kwargs.pop('shortcut', None)
    underline = kwargs.pop('underline', -1)
    tear = kwargs.pop('tear', False)
    state = kwargs.pop('state', None)
    image = kwargs.pop('image', None)
    icon = kwargs.pop('icon', None)
    align = kwargs.pop('align', 'left')
    if kind == 'menu':
        self.createMenu(menu, tearable=tear, showInBar=True)
    elif kind.startswith('sub'):
        self.addSubMenu(menu, name)
    elif kind.startswith('radio') or group is not None:
        self.addMenuRadioButton(menu, group, value=name, func=func, shortcut=shortcut, underline=underline)
    elif kind == 'button':
        if name is None and func is not None:
            self.addMenu(menu, func=func, shortcut=shortcut, underline=underline)
        elif name is None:
            self.createMenu(menu, tearable=tear, showInBar=True)
        elif isinstance(name, (list, tuple)):
            self.addMenuList(menu, name, func)
        else:
            self.addMenuItem(menu, name, func=func, kind=None, shortcut=shortcut, underline=underline)
    elif kind.startswith('sep'):
        self.addMenuSeparator(menu)
    elif kind.startswith('check') or kind.startswith('tick'):
        self.addMenuCheckBox(menu, name, func=func, shortcut=shortcut, underline=underline)
    if state is not None:
        if kind == 'menu' or kind.startswith('sub'):
            if state == 'disabled': self.disableMenu(menu, name)
            elif state == 'enabled': self.enableMenu(menu, name)
        else:
            if state == 'disabled': self.disableMenuItem(menu, name)
            elif state == 'enabled': self.enableMenuItem(menu, name)
    if image is not None: self.setMenuImage(menu, name, image, align=align)
    if icon is not None: self.setMenuIcon(menu, name, icon, align=align)

def message(

self, title, value=None, *args, **kwargs)

simpleGUI - adds, sets & gets messages all in one go

def message(self, title, value=None, *args, **kwargs):
    """ simpleGUI - adds, sets & gets messages all in one go """
    widgKind = WIDGET_NAMES.Message
    try: self.widgetManager.verify(WIDGET_NAMES.Message, title)
    except: # widget exists
        if value is not None: self.setMessage(title, value)
        msg = self.getMessage(title)
    else: # new widget
        kwargs = self._parsePos(kwargs.pop("pos", []), kwargs)
        msg = self._messageMaker(title, value, *args, **kwargs)
    if len(kwargs) > 0:
        self._configWidget(title, widgKind, **kwargs)
    return msg

def meter(

self, title, value=None, *args, **kwargs)

simpleGUI - adds, sets & gets meters all in one go

def meter(self, title, value=None, *args, **kwargs):
    """ simpleGUI - adds, sets & gets meters all in one go """
    widgKind = WIDGET_NAMES.Meter
    kind = kwargs.pop("kind","'meter")
    fill = kwargs.pop("fill", None)
    text = kwargs.pop("text", None)
    try: self.widgetManager.verify(WIDGET_NAMES.Meter, title)
    except: # widget exists
        meter =  self.getMeter(title)
    else: # new widget
        kwargs = self._parsePos(kwargs.pop("pos", []), kwargs)
        if kind == "split": meter = self._addMeter(title, "SPLIT", **kwargs)
        elif kind == "dual":  meter = self._addMeter(title, "DUAL", **kwargs)
        else: meter = self._addMeter(title, "METER", **kwargs)
    if value is not None: self.setMeter(title, value, text=text)
    if fill is not None: self.setMeterFill(title, fill)
    if len(kwargs) > 0:
        self._configWidget(title, widgKind, **kwargs)
    return meter

def microbit(

self, title, *args, **kwargs)

simpleGUI - adds, sets & gets microbits all in one go

def microbit(self, title, *args, **kwargs):
    '''simpleGUI - adds, sets & gets microbits all in one go'''
    widgKind = WIDGET_NAMES.MicroBit
    image = kwargs.pop("image", None)
    brightness = kwargs.pop("brightness", None)
    x = kwargs.pop("x", None)
    y = kwargs.pop("y", None)
    clear = kwargs.pop("clear", False)
    try: self.widgetManager.verify(widgKind, title)
    except: # widget exists
        mb = self.getMicroBit(title)
    else: # new widget
        kwargs = self._parsePos(kwargs.pop("pos", []), kwargs)
        mb = self.addMicroBit(title, *args, **kwargs)
    if image is not None: self.setMicroBitImage(title, image)
    if brightness is not None: self.setMicroBitPixel(title, x, y, brightness)
    if clear: self.clearMicroBit(title)
    if len(kwargs) > 0:
        self._configWidget(title, widgKind, **kwargs)
    return mb

def moveWidgetType(

self, kind, name, row=None, column=0, colspan=0, rowspan=0, sticky='we')

def moveWidgetType(self, kind, name, row=None, column=0, colspan=0, rowspan=0, sticky=W+E):
    self.hideWidgetType(kind, name, collapse=True)
    widget = self.widgetManager.get(kind, name)
    container = self.getContainer()
    if container != widget.master:
        widget = self._cloneWidget(widget, container)
        self.widgetManager.update(kind, name, widget)
    self._positionWidget(widget, row, column, colspan, rowspan, sticky, updateBg=False)
    return widget

def nextFrame(

self, title, callFunction=True)

def nextFrame(self, title, callFunction=True):
    self.widgetManager.get(WIDGET_NAMES.FrameStack, title).showNextFrame(callFunction)

def note(

*args, **kwds)

@contextmanager
def note(self, title, tabTitle=None, **kwargs):
    if tabTitle is None:
        note = self.startNote(title)
    else:
        note = self.openNote(title, tabTitle)
    self.configure(**kwargs)
    try: yield note
    finally: self.stopNote()

def notebook(

*args, **kwds)

@contextmanager
def notebook(self, title, row=None, column=0, colspan=0, rowspan=0, sticky="NSEW", **kwargs):
    try:
        note = self.startNotebook(title, row, column, colspan, rowspan, sticky)
    except ItemLookupError:
        note = self.openNotebook(title)
    self.configure(**kwargs)
    try: yield note
    finally: self.stopNotebook()

def numBox(

self, title='Number Box', question='Enter a number', parent=None)

def numBox(self, title="Number Box", question="Enter a number", parent=None):
    self.topLevel.update_idletasks()
    if parent is None:
        parent = self.topLevel
    else:
        parent = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent)
    return NumDialog(parent, title, question).result

def numberBox(

self, title='Number Box', question='Enter a number', parent=None)

def numberBox(self, title="Number Box", question="Enter a number", parent=None):
    return self.numBox(title, question, parent)

def okBox(

self, title, message, parent=None)

def okBox(self, title, message, parent=None):
    self.topLevel.update_idletasks()
    title, message = self._translatePopup(title, message)
    if parent is None:
        return MessageBox.askokcancel(title, message)
    else:
        parent = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent)
        opts = {"parent": parent}
        return MessageBox.askokcancel(title, message, **opts)

def openBox(

self, title=None, dirName=None, fileTypes=None, asFile=False, parent=None, multiple=False, mode='r')

def openBox(self, title=None, dirName=None, fileTypes=None, asFile=False, parent=None, multiple=False, mode='r'):
    self.topLevel.update_idletasks()
    # define options for opening
    options = {}
    if title is not None:
        options['title'] = title
    if dirName is not None:
        options['initialdir'] = dirName
    if fileTypes is not None:
        options['filetypes'] = fileTypes
    if parent is not None:
        options["parent"] = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent)
    if asFile:
        options["mode"] = mode
        if multiple: files = list(filedialog.askopenfiles(**options))
        else: files = filedialog.askopenfile(**options)
        return files
    # will return "" if cancelled
    else:
        if multiple: files = list(self.topLevel.tk.splitlist(filedialog.askopenfilenames(**options)))
        else: files = filedialog.askopenfilename(**options)
        return files

def openFrame(

self, title)

def openFrame(self, title):
    try: return self._openContainer(WIDGET_NAMES.Frame, title)
    except: return self._openContainer(WIDGET_NAMES.SubFrame, title)

def openFrameStack(

self, title)

def openFrameStack(self, title):
    return self._openContainer(WIDGET_NAMES.FrameStack, title)

def openLabelFrame(

self, title)

def openLabelFrame(self, title):
    return self._openContainer(WIDGET_NAMES.LabelFrame, title)

def openNote(

self, frameTitle, tabTitle)

def openNote(self, frameTitle, tabTitle):
    return self._openContainer(WIDGET_NAMES.Notebook, frameTitle+"__"+tabTitle)

def openNotebook(

self, title)

def openNotebook(self, title):
    return self._openContainer(WIDGET_NAMES.Notebook, title)

def openPage(

self, windowTitle, pageNumber)

def openPage(self, windowTitle, pageNumber):
    return self._openContainer(WIDGET_NAMES.Page, windowTitle+"__"+str(pageNumber))

def openPagedWindow(

self, title)

def openPagedWindow(self, title):
    return self._openContainer(WIDGET_NAMES.PagedWindow, title)

def openPane(

self, title)

def openPane(self, title):
    return self._openContainer(WIDGET_NAMES.Pane, title)

def openPanedFrame(

self, title)

def openPanedFrame(self, title):
    return self._openContainer(WIDGET_NAMES.PanedFrame, title)

def openRootPage(

self, title)

def openRootPage(self, title):
    return self._openContainer(WIDGET_NAMES.RootPage, title)

def openScrollPane(

self, title)

def openScrollPane(self, title):
    return self._openContainer(WIDGET_NAMES.ScrollPane, title)

def openSubFrame(

self, frameTitle, frameNumber)

def openSubFrame(self, frameTitle, frameNumber):
    return self._openContainer(WIDGET_NAMES.SubFrame, frameTitle+"__"+str(frameNumber))

def openSubWindow(

self, title)

def openSubWindow(self, title):
    return self._openContainer(WIDGET_NAMES.SubWindow, title)

def openTab(

self, frameTitle, tabTitle)

def openTab(self, frameTitle, tabTitle):
    return self._openContainer(WIDGET_NAMES.Tab, frameTitle+"__"+tabTitle)

def openTabbedFrame(

self, title)

def openTabbedFrame(self, title):
    return self._openContainer(WIDGET_NAMES.TabbedFrame, title)

def openToggleFrame(

self, title)

def openToggleFrame(self, title):
    return self._openContainer(WIDGET_NAMES.ToggleFrame, title)

def option(

self, title, value=None, *args, **kwargs)

simpleGUI - shortner for optionBox()

def option(self, title, value=None, *args, **kwargs):
    """ simpleGUI - shortner for optionBox() """
    return self.optionBox(title, value, *args, **kwargs)

def optionBox(

self, title, value=None, *args, **kwargs)

simpleGUI - adds, sets & gets optionBoxes all in one go

def optionBox(self, title, value=None, *args, **kwargs):
    """ simpleGUI - adds, sets & gets optionBoxes all in one go """
    widgKind = WIDGET_NAMES.OptionBox
    kind = kwargs.pop("kind", "standard").lower().strip()
    label = kwargs.pop("label", False)
    callFunction = kwargs.pop("callFunction", True)
    override = kwargs.pop("override", False)
    checked = kwargs.pop("checked", True)
    selected = kwargs.pop("selected", None)
    disabled = kwargs.pop("disabled", "-")
    # select=set, replace=change, rename=rename, clear=clear, delete=delete
    if value is None: mode = 'get'
    else: mode = 'select'
    mode = kwargs.pop("mode", mode)
    index = kwargs.pop("index", None)
    newName = kwargs.pop("newName", None)
    try: self.widgetManager.verify(WIDGET_NAMES.OptionBox, title)
    except: # widget exists
        if mode == "select":
            if value is not None: self.setOptionBox(title, index=value, value=True, callFunction=callFunction, override=override)
            else: gui.error("No item specified to select in optionBox: %s", title)
        elif mode == "deselect":
            if value is not None: self.setOptionBox(title, index=value, value=False, callFunction=callFunction, override=override)
            else:
                self.clearOptionBox(title, callFunction=callFunction)
                gui.info("optionBox set back to its original state: %s", title)
        elif mode == "toggle":
            gui.error("Toggling optionboxes not supported: %s", title)
        elif mode == "clear":
            if value is not None: gui.error("No value should be specified wen clearing optionBox: %s", title)
            else: self.clearOptionBox(title, callFunction=callFunction)
        elif mode == "rename":
            if value is not None: self.renameOptionBox(title, item=value, newName=newName, callFunction=callFunction)
            else: gui.error("No item specified to rename in optionBox: %s", title)
        elif mode == "replace":
            if value is not None: self.changeOptionBox(title, options=value, index=index, callFunction=callFunction)
            else: gui.error("No values specified to replace in optionBox: %s", title)
        elif mode == "delete":
            if value is not None: self.deleteOptionBox(title, index=value)
            else: gui.error("No item specified to delete in optionBox: %s", title)
        elif mode == "get":
            pass
        else:
            gui.error("Invalid mode (%s) specified in optionBox: %s", mode, title)
        opt =  self.getOptionBox(title)
    else: # new widget
        kwargs = self._parsePos(kwargs.pop("pos", []), kwargs)
        if kind == "ticks":
            if label: opt = self.addLabelTickOptionBox(title, value, *args, label=label, disabled=disabled, **kwargs)
            else: opt = self.addTickOptionBox(title, value, *args, disabled=disabled, **kwargs)
        else:
            if label: opt = self.addLabelOptionBox(title, value, *args, label=label, disabled=disabled, **kwargs)
            else: opt = self.addOptionBox(title, value, *args, disabled=disabled, **kwargs)
            if selected is not None: self.setOptionBox(title, selected)
    if len(kwargs) > 0:
        self._configWidget(title, widgKind, **kwargs)
    return opt

def optionbox(

self, title, value=None, *args, **kwargs)

simpleGUI - shortner for optionBox()

def optionbox(self, title, value=None, *args, **kwargs):
    """ simpleGUI - shortner for optionBox() """
    return self.optionBox(title, value, *args, **kwargs)

def page(

*args, **kwds)

@contextmanager
def page(self, windowTitle=None, pageNumber=None, sticky="nw", **kwargs):
    if windowTitle is None:
        pg = self.startPage(sticky)
    else:
        pg = self.openPage(windowTitle, pageNumber)
    self.configure(**kwargs)
    try: yield pg
    finally: self.stopPage()

def pagedWindow(

*args, **kwds)

@contextmanager
def pagedWindow(self, title, row=None, column=0, colspan=0, rowspan=0, **kwargs):
    try:
        pw = self.startPagedWindow(title, row, column, colspan, rowspan)
    except ItemLookupError:
        pw = self.openPagedWindow(title)
    self.configure(**kwargs)
    try: yield pw
    finally: self.stopPagedWindow()

def panedFrame(

*args, **kwds)

@contextmanager
def panedFrame(self, title, row=None, column=0, colspan=0, rowspan=0, sticky="NSEW", **kwargs):
    vertical = kwargs.pop("vertical", False)
    sash = kwargs.pop("sash", 50)
    reOpen = False
    try:
        pane = self.startPanedFrame(title, row, column, colspan, rowspan, sticky)
    except ItemLookupError:
        reOpen = True
        pane = self.openPane(title)
    if vertical: self.setPanedFrameVertical(title)
    self.configure(**kwargs)
    try: yield pane
    finally:
        if reOpen:
            self.stopContainer()
        else:
            self.stopPanedFrame()
            self.setPaneSashPosition(sash, pane)

def panedFrameVertical(

*args, **kwds)

@contextmanager
def panedFrameVertical(self, title, row=None, column=0, colspan=0, rowspan=0, sticky="NSEW", **kwargs):
    gui.warn('Setting panedFrameVertical(%s) is deprecated, please use panedFrame(vertical=True)', title)
    reOpen = False
    sash = kwargs.pop("sash", 50)
    try:
        pane = self.startPanedFrameVertical(title, row, column, colspan, rowspan, sticky)
    except ItemLookupError:
        reOpen = True
        pane = self.openPane(title)
    self.configure(**kwargs)
    try: yield pane
    finally:
        if reOpen:
            self.stopContainer()
        else:
            self.stopPanedFrame()
            self.setPaneSashPosition(sash, pane)

def pie(

self, title, value=None, *args, **kwargs)

simpleGUI - adds, sets & gets pies all in one go

def pie(self, title, value=None, *args, **kwargs):
    """ simpleGUI - adds, sets & gets pies all in one go """
    widgKind = WIDGET_NAMES.PieChart
    name = kwargs.pop("name", None)
    try: self.widgetManager.verify(widgKind, title)
    except: # widget exists
        if name is not None: self.setPieChart(title, name, value)
        pie = self.getPieChart(title)
    else: # new widget
        kwargs = self._parsePos(kwargs.pop("pos", []), kwargs)
        pie = self.addPieChart(title, value, *args, **kwargs)
    if len(kwargs) > 0:
        self._configWidget(title, widgKind, **kwargs)
    return pie

def playNote(

self, note, duration=200)

def playNote(self, note, duration=200):
    self._loadWinsound()
    if self.platform == self.WINDOWS and winsound is not False:
        try:
            if isinstance(note, UNIVERSAL_STRING):
                freq = self.NOTES[note.lower()]
            else:
                freq = note
        except KeyError:
            raise Exception("Error: cannot play note - " + note)
        try:
            if isinstance(duration, UNIVERSAL_STRING):
                length = self.DURATIONS[duration.upper()]
            else:
                length = duration
        except KeyError:
            raise Exception("Error: cannot play duration - " + duration)
        try:
            winsound.Beep(freq, length)
        except RuntimeError:
            raise Exception(
                "Sound not available on this platform: " +
                platform())
    else:
        # sound not available at this time
        raise Exception(
            "Sound not supported on this platform: " +
            platform())

def playSound(

self, sound, wait=False)

def playSound(self, sound, wait=False):
    self._soundWrap(sound, True, False, wait)

def plot(

self, title, t=None, s=None, *args, **kwargs)

simpleGUI - adds, sets & gets plots all in one go

def plot(self, title, t=None, s=None, *args, **kwargs):
    """ simpleGUI - adds, sets & gets plots all in one go """
    widgKind = WIDGET_NAMES.Plot
    nav = kwargs.pop("nav", kwargs.pop("showNav", False))
    try: self.widgetManager.verify(widgKind, title)
    except: # widget exists
        keepLabels = kwargs.pop("keepLabels", False)
        self.updatePlot(title, t, s, keepLabels=keepLabels)
        plot = self.widgetManager.get(WIDGET_NAMES.Plot, title).axes
    else: # new widget
        kwargs = self._parsePos(kwargs.pop("pos", []), kwargs)
        if t is not None:
            if s is not None:
                plot = self.addPlot(title, t, s, *args, showNav=nav, **kwargs)
            else:
                gui.warn("Invalid parameters for plot: must provide t & s")
                return None
        else:
            plot = self.addPlotFig(title, *args, showNav=nav, **kwargs)
    return plot

def popUp(

self, title, message=None, kind='info', parent=None)

simpleGUI - shortener for the various popUps

def popUp(self, title, message=None, kind="info", parent=None):
    """ simpleGUI - shortener for the various popUps """
    if message is None:
        message = title
        title = kind.capitalize() + " Dialog"
    if kind == "info": return self.infoBox(title, message, parent)
    elif kind == "error": return self.errorBox(title, message, parent)
    elif kind == "warning": return self.warningBox(title, message, parent)
    elif kind == "yesno": return self.yesNoBox(title, message, parent)
    elif kind == "question": return self.questionBox(title, message, parent)
    elif kind == "ok": return self.okBox(title, message, parent)
    elif kind == "retry": return self.retryBox(title, message, parent)
    elif kind == "string": return self.stringBox(title, message, parent)
    elif kind == "integer": return self.integerBox(title, message, parent)
    elif kind == "float": return self.floatBox(title, message, parent)
    elif kind == "text": return self.textBox(title, message, parent)
    elif kind == "number": return self.numberBox(title, message, parent)
    else: gui.error("Invalid popUp kind: %s, with title: %s", kind, title)

def prevFrame(

self, title, callFunction=True)

def prevFrame(self, title, callFunction=True):
    self.widgetManager.get(WIDGET_NAMES.FrameStack, title).showPrevFrame(callFunction)

def prompt(

self, title, message, kind='string', parent=None)

def prompt(self, title, message, kind="string", parent=None):
    return self.popUp(title, message, kind, parent)

def properties(

self, title, value=None, *args, **kwargs)

simpleGUI - adds, sets & gets properties all in one go

def properties(self, title, value=None, *args, **kwargs):
    """ simpleGUI - adds, sets & gets properties all in one go """
    widgKind = WIDGET_NAMES.Properties
    try: self.widgetManager.verify(widgKind, title)
    except: # widget exists
         if value is not None:
ed to work out args...
             self.setProperty(title, prop=value)
        props = self.getProperties(title)
    else: # new widget
        kwargs = self._parsePos(kwargs.pop("pos", []), kwargs)
        props = self.addProperties(title, value, *args, **kwargs)
    if len(kwargs) > 0:
        self._configWidget(title, widgKind, **kwargs)
    return props

def questionBox(

self, title, message, parent=None)

def questionBox(self, title, message, parent=None):
    self.topLevel.update_idletasks()
    if parent is None:
        return True if MessageBox.askquestion(title, message).lower() == "yes" else False
    else:
        parent = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent)
        opts = {"parent": parent}
        return True if MessageBox.askquestion(title, message, **opts).lower() == "yes" else False

def queueFunction(

self, func, *args, **kwargs)

adds the specified function & arguments to the event queue Functions in the event queue are actioned by the gui's main thread

:param func: the function to call :param args: any number of ordered arguments :param *kwargs: any number of named arguments :raises Full: if unable to add the function to the queue

def queueFunction(self, func, *args, **kwargs):
    """ adds the specified function & arguments to the event queue
    Functions in the event queue are actioned by the gui's main thread
    :param func: the function to call
    :param *args: any number of ordered arguments
    :param **kwargs: any number of named arguments
    :raises Full: if unable to add the function to the queue
    """
    self._loadThreading()
    if Queue is False:
        gui.warn("Unable to queueFunction - threading not possible.")
    else:
        self.eventQueue.put((5, func, args, kwargs), block=False)

def queuePriorityFunction(

self, func, *args, **kwargs)

queues the function with a higher priority - not working yet

def queuePriorityFunction(self, func, *args, **kwargs):
    """ queues the function with a higher priority - not working yet """
    self._loadThreading()
    if Queue is False:
        gui.warn("Unable to queueFunction - threading not possible.")
    else:
        self.eventQueue.put((1, func, args, kwargs), block=False)

def radio(

self, title, name=None, *args, **kwargs)

simpleGUI - shortner for radioButton()

def radio(self, title, name=None, *args, **kwargs):
    """ simpleGUI - shortner for radioButton() """
    return self.radioButton(title, name, *args, **kwargs)

def radioButton(

self, title, name=None, *args, **kwargs)

simpleGUI - adds, sets & gets radioButtons all in one go

def radioButton(self, title, name=None, *args, **kwargs):
    """ simpleGUI - adds, sets & gets radioButtons all in one go """
    widgKind = WIDGET_NAMES.RadioButton
    selected = kwargs.pop("selected", False)
    callFunction = kwargs.pop("callFunction", True)
    change = kwargs.pop("change", None)
    kind = kwargs.pop('kind', 'standard')
    # need slightly different approach, as use two params
    if name is None: return self.getRadioButton(title) # no name = get
    else:
        ident = title + "-" + name
        try: self.widgetManager.verify(widgKind, ident)
        except:
            self.setRadioButton(title, name, callFunction=callFunction)
            rb = self.getRadioButton(title)
            selected = False
        else:
            kwargs = self._parsePos(kwargs.pop("pos", []), kwargs)
            rb = self._radioButtonMaker(title, name, *args, **kwargs)
        if selected: self.setRadioButton(title, name)
        if change is not None: self.setRadioButtonChangeFunction(title, change)
        if kind == "square":
            if self.platform == self.MAC:
                gui.warn("Square radiobuttons not available on Mac, for radiobutton %s", title)
            elif not self.ttkFlag:
                rb.config(indicatoron=0)
            else:
                gui.warn("Square radiobuttons not available in ttk, for radiobutton %s", title)
        if len(kwargs) > 0:
            self._configWidget(ident, widgKind, **kwargs)
        return rb

def raiseFrame(

self, title)

will bring the named frame in front of any others

def raiseFrame(self, title):
    ''' will bring the named frame in front of any others '''
    gui.trace("Raising frame: %s", title)
    self.widgetManager.get(WIDGET_NAMES.Frame, title).lift()

def refreshDbGrid(

self, title)

def refreshDbGrid(self, title):
    gui.warn("Deprecated - grids renamed to tables")
    return self.refreshDbTable(title)

def refreshDbOptionBox(

self, title, selected=None)

def refreshDbOptionBox(self, title, selected=None):
    opt = self.widgetManager.get(WIDGET_NAMES.OptionBox, title)
    data = self._getDbTables(opt.db)
    self.changeOptionBox(title, data)
    if selected is not None:
        self.setOptionBox(title, selected)

def refreshDbTable(

self, title)

def refreshDbTable(self, title):
    grid = self.widgetManager.get(WIDGET_NAMES.Table, title)
    self._importSqlite3()
    if not sqlite3:
        self.error("Unable to load DB data - can't load sqlite3")
        return
    with sqlite3.connect(grid.db) as conn:
        cursor = conn.cursor()
        dataQuery = 'SELECT * from ' + grid.dbTable
        # select all data
        cursor.execute(dataQuery)
        self.replaceAllTableRows(title, cursor)

def refreshPlot(

self, title)

def refreshPlot(self, title):
    canvas = self.widgetManager.get(WIDGET_NAMES.Plot, title)
    canvas.draw()

def registerEvent(

self, func)

Queue a function, to be executed every poll time

def registerEvent(self, func):
    """ Queue a function, to be executed every poll time """
    self.events.append(func)

def reloadImage(

self, name, imageFile)

def reloadImage(self, name, imageFile):
    label = self.widgetManager.get(WIDGET_NAMES.Image, name)
    image = self._getImage(imageFile, False)
    self._populateImage(name, image)

def reloadImageData(

self, name, imageData, fmt='gif')

def reloadImageData(self, name, imageData, fmt="gif"):
    self.setImageData(name, imageData, fmt)

def removeAllWidgets(

self, current=False, sub=False)

def removeAllWidgets(self, current=False, sub=False):
    if current:
        self.emptyCurrentContainer()
    else:
        gui.trace('Removing all widgets from appJar')
        if sub: self.destroyAllSubWindows()
        containerData = self.containerStack[0]
        container = containerData['container']
        self._emptyContainerObj(container)
        # reset container values
        containerData = self._prepContainer(containerData["title"], containerData["type"], containerData["container"], 0, 1)
        self.containerStack[0] = containerData

def removeAutoEntry(

self, title, value)

def removeAutoEntry(self, title, value):
    entry = self.widgetManager.get(WIDGET_NAMES.Entry, title)
    try:
        entry.removeWord(value)
    except AttributeError:
        gui.error("You can only remove items from an AutoEntry, %s is not an AutoEntry.", title)

def removeBgImage(

self)

def removeBgImage(self):
    self.bgLabel.config(image="")
    # self.containerStack[0]['container'].config(image=None) # window as a
    # label doesn't work...
    # remove the reference - shouldn't be cached
    self.containerStack[0]['container'].image = None

def removeGoogleMapMarker(

self, title, label)

def removeGoogleMapMarker(self, title, label):
    gMap = self.widgetManager.get(WIDGET_NAMES.Map, title)
    if len(label) == 0:
        gMap.removeMarkers()
    else:
        gMap.removeMarker(label)

def removeListItem(

self, title, item)

def removeListItem(self, title, item):
    lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title)
    positions = self._getListPositions(title, item)
    if len(positions) > 0:
        lb.delete(positions[0])
    # show & select this item
    if positions[0] >= lb.size():
        positions[0] -= 1
    self.selectListItemAtPos(title, positions[0])

def removeListItemAtPos(

self, title, pos)

def removeListItemAtPos(self, title, pos):
    lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title)
    items = lb.get(0, END)
    if pos >= len(items):
        raise Exception("Invalid position: " + str(pos) + " must be between 0 and " + str(len(items)-1))
    lb.delete(pos)
    # show & select this item
    if pos >= lb.size():
        pos -= 1
    self.selectListItemAtPos(title, pos)

def removeStatusbar(

self)

def removeStatusbar(self):
    if self.hasStatus:
        while len(self._statusFields) > 0:
            self.removeStatusbarField(0)
        self.statusFrame.pack_forget()
        self.statusFrame.destroy()
        self.hasStatus = False
        self.header = ""

def removeStatusbarField(

self, field)

def removeStatusbarField(self, field):
    if self.hasStatus and field < len(self._statusFields):
        self._statusFields[field].pack_forget()
        self._statusFields[field].destroy()
        del self._statusFields[field]
    else:
        raise ItemLookupError("Invalid field number for statusbar: " + str(field))

def removeToolbar(

self, hide=True)

def removeToolbar(self, hide=True):
    while len(self.widgetManager.group(WIDGET_NAMES.Toolbar)) > 0:
        self.removeToolbarButton(list(self.widgetManager.group(WIDGET_NAMES.Toolbar))[0], hide)

def removeToolbarButton(

self, name, hide=True)

def removeToolbarButton(self, name, hide=True):
    if (name not in self.widgetManager.group(WIDGET_NAMES.Toolbar)):
        raise Exception("Unknown toolbar name: " + name)
    self.widgetManager.get(WIDGET_NAMES.Toolbar, name).destroy()
    self.widgetManager.remove(WIDGET_NAMES.Toolbar, name)
    if hide:
        if len(self.widgetManager.group(WIDGET_NAMES.Toolbar)) == 0:
            self.tb.pack_forget()
            self.tb.inUse = False
        if self.tb.toolbarMin is not None:
            self.tb.toolbarMin.pack_forget()

def removeWidgetType(

self, kind, name, collapse=False)

def removeWidgetType(self, kind, name, collapse=False):
    if kind == WIDGET_NAMES.RadioButton:
        gui.error("Can't remove widget %s - %s", kind, name)
        return
        
    item = self.widgetManager.get(kind, name)
    # if it's a flasher, remove it
    if item in self.widgetManager.group(WIDGET_NAMES.FlashLabel):
        gui.trace("Remove flash label: %s", name)
        self.widgetManager.remove(WIDGET_NAMES.FlashLabel, item)
        if len(self.widgetManager.group(WIDGET_NAMES.FlashLabel)) == 0:
            self.doFlash = False
    # animated images...
    if self._widgetHasContainer(kind, item):
        gui.trace("Remove widget (%s) in container: %s", kind, name)
        parent = item.master
        # is it a container in a labelBox?
        # if so - remove & destroy the labelBox
        if hasattr(parent, "inContainer") and parent.inContainer:
            gui.trace("Container in container")
            labParent = parent.master
            self.widgetManager.remove(WIDGET_NAMES.FrameBox, labParent)
            self.widgetManager.remove(WIDGET_NAMES.Label, name)
            self.widgetManager.remove(WIDGET_NAMES.FrameLabel, name)
            labParent.grid_forget()
            labParent.destroy()
        # otherwise destroy this container & a label if we have one
        else:
            parent.grid_forget()
            parent.destroy()
            try:
                self.widgetManager.remove(WIDGET_NAMES.Label, name)
                self.widgetManager.remove(WIDGET_NAMES.FrameLabel, name)
            except: pass
        self.widgetManager.remove(WIDGET_NAMES.FrameBox, parent)
    else:
        gui.trace("Remove widget: %s", name)
        item.grid_forget()
        self.cleanseWidgets(item)

def renameMenu(

self, title, newName)

def renameMenu(self, title, newName):
    theMenu = self.widgetManager.get(WIDGET_NAMES.Menu, title)
    try:
        self.menuBar.entryconfigure(title, label=newName)
    except TclError:
        gui.error("Unable to rename menu: %s - item not found", title)

def renameMenuItem(

self, title, item, newName)

def renameMenuItem(self, title, item, newName):
    theMenu = self.widgetManager.get(WIDGET_NAMES.Menu, title)
    try:
        theMenu.entryconfigure(item, label=newName)
    except TclError:
        gui.error("Unable to rename menu item: %s, in menu: %s - item not found", item, title)

def renameOptionBoxItem(

self, title, item, newName=None, callFunction=False)

Changes the text of the specified item in the named OptionBox :param title: the OptionBox to change :param item: the item to rename :param newName: the value to rename it with :param callFunction: whether to generate an event to notify that the widget has changed :returns: None :raises ItemLookupError: if the title can't be found

def renameOptionBoxItem(self, title, item, newName=None, callFunction=False):
    """ Changes the text of the specified item in the named OptionBox
    :param title: the OptionBox to change
    :param item: the item to rename
    :param newName: the value to rename it with
    :param callFunction: whether to generate an event to notify that the widget has changed
    :returns: None
    :raises ItemLookupError: if the title can't be found
    """
    self.widgetManager.check(WIDGET_NAMES.OptionBox, title, group=WidgetManager.VARS)
    self.setOptionBox(title, item, value=newName, callFunction=callFunction)

def replaceAllGridRows(

self, title, data)

def replaceAllGridRows(self, title, data):
    return self.replaceAllTableRows(title, data)

def replaceAllTableRows(

self, title, data, deleteHeader=True)

def replaceAllTableRows(self, title, data, deleteHeader=True):
    grid = self.widgetManager.get(WIDGET_NAMES.Table, title)
    grid.deleteAllRows(deleteHeader=deleteHeader)
    grid.addRows(data, scroll=False)

def replaceDbGrid(

self, title, db, table)

def replaceDbGrid(self, title, db, table):
    gui.warn("Deprecated - grids renamed to tables")
    return self.replaceDbTable(title, db, table)

def replaceDbTable(

self, title, db, table)

def replaceDbTable(self, title, db, table):
    grid = self.widgetManager.get(WIDGET_NAMES.Table, title)
    grid.db = db
    grid.dbTable = table
    self._importSqlite3()
    if not sqlite3:
        self.error("Unable to load DB data - can't load sqlite3")
        return
    with sqlite3.connect(db) as conn:
        cursor = conn.cursor()
        dataQuery = 'SELECT * from ' + table
        # select all data
        cursor.execute(dataQuery)
        self.setTableHeaders(title, cursor)
        self.replaceAllTableRows(title, cursor)
    self.topLevel.update_idletasks()

def replaceGridRow(

self, title, rowNum, data)

def replaceGridRow(self, title, rowNum, data):
    return self.replaceTableRow(title, rowNum, data)

def replaceTableRow(

self, title, rowNum, data)

def replaceTableRow(self, title, rowNum, data):
    grid = self.widgetManager.get(WIDGET_NAMES.Table, title)
    grid.replaceRow(rowNum, data)

def resetAllProperties(

self, callFunction=False)

def resetAllProperties(self, callFunction=False):
    props = {}
    for k in self.widgetManager.group(WIDGET_NAMES.Properties):
        self.resetProperties(k, callFunction)

def resetProperties(

self, title, callFunction=True)

def resetProperties(self, title, callFunction=True):
    props = self.widgetManager.get(WIDGET_NAMES.Properties, title)
    props.resetProperties(callFunction)

def resizeBgImage(

self)

def resizeBgImage(self):
    if self.containerStack[0]['container'].image is None:
        return
    else:
        pass

def retryBox(

self, title, message, parent=None)

def retryBox(self, title, message, parent=None):
    self.topLevel.update_idletasks()
    if parent is None:
        return MessageBox.askretrycancel(title, message)
    else:
        parent = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent)
        opts = {"parent": parent}
        return MessageBox.askretrycancel(title, message, **opts)

def saveBox(

self, title=None, fileName=None, dirName=None, fileExt='.txt', fileTypes=None, asFile=False, parent=None)

def saveBox( self, title=None, fileName=None, dirName=None, fileExt=".txt",
        fileTypes=None, asFile=False, parent=None):
    self.topLevel.update_idletasks()
    if fileTypes is None:
        fileTypes = [('all files', '.*'), ('text files', '.txt')]
    # define options for opening
    options = {}
    options['defaultextension'] = fileExt
    options['filetypes'] = fileTypes
    options['initialdir'] = dirName
    options['initialfile'] = fileName
    options['title'] = title
    if parent is not None:
        options["parent"] = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent)
    if asFile:
        return filedialog.asksaveasfile(mode='w', **options)
    # will return "" if cancelled
    else:
        return filedialog.asksaveasfilename(**options)

def saveGoogleMap(

self, title, fileLocation)

def saveGoogleMap(self, title, fileLocation):
    gMap = self.widgetManager.get(WIDGET_NAMES.Map, title)
    return gMap.saveTile(fileLocation)

def saveSettings(

self, fileName='appJar.ini')

saves the current settings to a file called automatically by stop() of settings were loaded at start

def saveSettings(self, fileName="appJar.ini"):
    """ saves the current settings to a file
        called automatically by stop() of settings were loaded at start """
    self._loadConfigParser()
    if not ConfigParser:
        self.error("Unable to save config file - no configparser")
        return
    settings = ConfigParser()
    settings.optionxform = str
    settings.add_section('GEOM')
    geom = self.topLevel.geometry()
    ms = self.topLevel.minsize()
    ms = "%s,%s" % (ms[0], ms[1])
    settings.set('GEOM', 'geometry', geom)
    gui.trace("Save geom as: %s", geom)
    settings.set('GEOM', 'minsize', ms)
    settings.set('GEOM', "fullscreen", str(self.topLevel.attributes('-fullscreen')))
    settings.set('GEOM', "state", str(self.topLevel.state()))
    # get toolbar setting
    if self.tb.inUse:
        gui.trace("Saving toolbar settings")
        settings.add_section("TOOLBAR")
        settings.set("TOOLBAR", "pinned", str(self.tb.pinned))
    # get container settings
    for k, v in self.widgetManager.group(WIDGET_NAMES.ToggleFrame).items():
        gui.trace("Saving toggle %s", k)
        if "TOGGLES" not in settings.sections(): settings.add_section("TOGGLES")
        settings.set("TOGGLES", k, str(v.isShowing()))
    for k, v in self.widgetManager.group(WIDGET_NAMES.TabbedFrame).items():
        gui.trace("Saving tab %s", k)
        if "TABS" not in settings.sections(): settings.add_section("TABS")
        settings.set("TABS", k, str(v.getSelectedTab()))
    for k, v in self.widgetManager.group(WIDGET_NAMES.PagedWindow).items():
        gui.trace("Saving page %s", k)
        if "PAGES" not in settings.sections(): settings.add_section("PAGES")
        settings.set("PAGES", k, str(v.getPageNumber()))
    for k, v in self.widgetManager.group(WIDGET_NAMES.SubWindow).items():
        if "SUBWINDOWS" not in settings.sections(): settings.add_section("SUBWINDOWS")
        if v.shown:
            v.update()
            settings.set("SUBWINDOWS", k, "True")
            settings.add_section(k)
            settings.set(k, "geometry", v.geometry())
            ms = v.minsize()
            settings.set(k, 'minsize', "%s,%s" % (ms[0], ms[1]))
            settings.set(k, "state", v.state())
            gui.trace("Saving subWindow %s: geom=%s, state=%s, minsize=%s", k, v.geometry(), v.state(), ms)
        else:
            settings.set("SUBWINDOWS", k, "False")
            gui.trace("Skipping subwindow: %s", k)
    for k, v in self.externalSettings.items():
        if "EXTERNAL" not in settings.sections(): settings.add_section("EXTERNAL")
        settings.set("EXTERNAL", k, str(v))
    # pane positions?
    # sub windows geom & visibility
    # scrollpane x & y positions
    # language
    # ttk
    # debug level
    with open(fileName, 'w') as theFile:
        settings.write(theFile)

def scale(

self, title, *args, **kwargs)

simpleGUI - adds, sets & gets scales all in one go

def scale(self, title, *args, **kwargs):
    """ simpleGUI - adds, sets & gets scales all in one go """
    widgKind = WIDGET_NAMES.Scale
    vert = kwargs.pop("direction", "horizontal").lower() == "vertical"
    increment = kwargs.pop("increment", None)
    value = kwargs.pop("value", None)
    interval = kwargs.pop("interval", None)
    show = kwargs.pop("show", False)
    _range = kwargs.pop("range", None)
    callFunction = kwargs.pop("callFunction", True)
    label = kwargs.pop("label", False)
    try: self.widgetManager.verify(widgKind, title)
    except: # widget exists
        scale = self.getScale(title)
    else: # new widget
        kwargs = self._parsePos(kwargs.pop("pos", []), kwargs)
        scale = self._scaleMaker(title, label, *args, **kwargs)
    if _range is not None: self.setScaleRange(title, _range[0], _range[1])
    if vert: self.setScaleVertical(title)
    if increment is not None: self.setScaleIncrement(title, increment)
    if interval is not None: self.showScaleIntervals(title, interval)
    if show: self.showScaleValue(title)
    if value is not None: self.setScale(title, value, callFunction)
    if len(kwargs) > 0:
        self._configWidget(title, widgKind, **kwargs)
    return scale

def scrollPane(

*args, **kwds)

@contextmanager
def scrollPane(self, title, row=None, column=0, colspan=0, rowspan=0, sticky="NSEW", **kwargs):
    disabled = kwargs.pop("disabled", "")
    try:
        sp = self.startScrollPane(title, row, column, colspan, rowspan, sticky, disabled)
    except ItemLookupError:
        sp = self.openScrollPane(title)
    self.configure(**kwargs)
    try: yield sp
    finally: self.stopScrollPane()

def searchGoogleMap(

self, title, location)

def searchGoogleMap(self, title, location):
    gMap = self.widgetManager.get(WIDGET_NAMES.Map, title)
    gMap.changeLocation(location)

def searchTextArea(

self, title, pattern, start=None, stop=None, nocase=True, backwards=False)

will find and highlight the specified text, returning the position

def searchTextArea(self, title, pattern, start=None, stop=None, nocase=True, backwards=False):
    """ will find and highlight the specified text, returning the position """
    ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title)
    if start is None: start = ta.index(INSERT)
    pos = ta.search(pattern, start, stopindex=stop, nocase=nocase, backwards=backwards)
    ta.focus_set()
    if pos == "":
        return None
    else:
        end = str(pos) + " + " + str(len(pattern)) + " c"
        ta.see(pos)
        ta.tag_add(SEL, pos, end)
        ta.mark_set("insert", pos)
        return pos

def selectFrame(

self, title, num, callFunction=True)

def selectFrame(self, title, num, callFunction=True):
    if type(num) in (list, tuple): num = num[0]
    num = int(num)
    self.widgetManager.get(WIDGET_NAMES.FrameStack, title).showFrame(num, callFunction)

def selectGridColumn(

self, title, col, highlight=None)

def selectGridColumn(self, title, col, highlight=None):
    return self.selectTableColumn(title, col, highlight)

def selectGridRow(

self, title, row, highlight=None)

def selectGridRow(self, title, row, highlight=None):
    gui.warn("Deprecated - grids renamed to tables")
    return self.selectTableRow(title, row, highlight)

def selectListItem(

self, title, item, callFunction=True)

def selectListItem(self, title, item, callFunction=True):
    lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title)
    positions = self._getListPositions(title, item)
    if len(positions) > 1 and lb.cget("selectmode") == EXTENDED:
        allOk = True
        for pos in positions:
            if not self.selectListItemAtPos(title, pos, callFunction):
                allOk = False
        return allOk
    elif len(positions) > 1:
        gui.warn("Unable to select multiple items for list: %s. Selecting first item: %s", title, item[0])
        return self.selectListItemAtPos(title, positions[0], callFunction)
    elif len(positions) == 1:
        return self.selectListItemAtPos(title, positions[0], callFunction)
    else:
        gui.warn("Invalid list item(s): %s for list: %s", item, title)
        return False

def selectListItemAtPos(

self, title, pos, callFunction=False)

def selectListItemAtPos(self, title, pos, callFunction=False):
    lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title)
    if lb.size() == 0:
        gui.warn("No items in list: %s, unable to select item at pos: %s", title, pos)
        return False
    if pos < 0 or pos > lb.size() - 1:
        gui.warn("Invalid list position: %s for list: %s (max: %s)", pos, title, lb.size()-1)
        return False
    # clear previous selection if we're not multi
    if lb.cget("selectmode") != EXTENDED:
        lb.selection_clear(0, END)
    # show & select this item
    lb.see(pos)
    lb.activate(pos)
    lb.selection_set(pos)
    # now call function
    if callFunction and hasattr(lb, 'cmd'):
        lb.cmd()
    self.topLevel.update_idletasks()
    return True

def selectTableColumn(

self, title, col, highlight=None)

def selectTableColumn(self, title, col, highlight=None):
    grid = self.widgetManager.get(WIDGET_NAMES.Table, title)
    grid.selectColumn(col, highlight)

def selectTableRow(

self, title, row, highlight=None)

def selectTableRow(self, title, row, highlight=None):
    grid = self.widgetManager.get(WIDGET_NAMES.Table, title)
    grid.selectRow(row, highlight)

def separator(

self, *args, **kwargs)

simpleGUI - adds horizontal/vertical separators

def separator(self, *args, **kwargs):
    """ simpleGUI - adds horizontal/vertical separators """
    direction = kwargs.pop("direction", "horizontal").lower()
    kwargs = self._parsePos(kwargs.pop("pos", []), kwargs)
    if direction == "vertical":
        return self.addVerticalSeparator(*args, **kwargs)
    else:
        return self.addHorizontalSeparator(*args, **kwargs)

def setAnimationSpeed(

self, name, speed)

def setAnimationSpeed(self, name, speed):
    img = self.widgetManager.get(WIDGET_NAMES.Image, name).image
    if speed < 1:
        speed = 1
        self.warn("Setting %s speed to 1. Minimum animation speed is 1.", name)
    img.anim_speed = int(speed)

def setAutoEntryNumRows(

self, title, rows)

def setAutoEntryNumRows(self, title, rows):
    entry = self.widgetManager.get(WIDGET_NAMES.Entry, title)
    try:
        entry.setNumRows(rows)
    except AttributeError:
        gui.error("You can only change the number of rows in an AutoEntry, %s is not an AutoEntry.", title)

def setBg(

self, colour, override=False, tint=False)

def setBg(self, colour, override=False, tint=False):
    if not self.ttkFlag:
        if self._getContainerProperty('type') == WIDGET_NAMES.RootPage:
moved this - it makes the screen do funny stuff
             self.appWindow.config(background=colour)
            self.bgLabel.config(background=colour)
        self._getContainerProperty('container').config(background=colour)
        for child in self._getContainerProperty('container').winfo_children():
            if not self._isWidgetContainer(child):
                # horrible hack to deal with weird ScrolledText
                # winfo_children returns ScrolledText as a Frame
                # therefore can't call some functions
                # this gets the ScrolledText version
                if gui.GET_WIDGET_CLASS(child) == "Frame":
                    for val in self.widgetManager.group(WIDGET_NAMES.TextArea).values():
                        if str(val) == str(child):
                            child = val
                            break
                gui.SET_WIDGET_BG(child, colour, override, tint)
    else:
        gui.trace("In ttk mode - trying to set BG to %s", colour)
        self.ttkStyle.configure(".", background=colour)

def setBgImage(

self, image)

def setBgImage(self, image):
    image = self._getImage(image, False, False)  # make sure it's not using the cache
    # self.containerStack[0]['container'].config(image=image) # window as a
    # label doesn't work...
    self.bgLabel.config(image=image)
    self.containerStack[0]['container'].image = image  # keep a reference!

def setButton(

self, name, text)

def setButton(self, name, text):
    but = self.widgetManager.get(WIDGET_NAMES.Button, name)
    try: # try to bind a function
        command = self.MAKE_FUNC(text, name)
        but.config(command=command)
    except: # otherwise change the text
        but.config(text=text)

def setButtonFont(

self, *args, **kwargs)

def setButtonFont(self, *args, **kwargs):
    self._fontHelper('buttonFont', *args, **kwargs)

def setButtonImage(

self, name, imgFile, align=None)

def setButtonImage(self, name, imgFile, align=None):
    but = self.widgetManager.get(WIDGET_NAMES.Button, name)
    image = self._getImage(imgFile)
    # works on Mac & Windows :)
    if align == None:
        but.config(image=image, text="")
        if not self.ttk:
            but.config(justify=LEFT, compound=TOP)
        else:
            but.config(compound=CENTER)
    else:
        but.config(image=image, compound=align)
    # but.config(image=image, compound=None, text="") # works on Windows, not Mac
    but.image = image

def setCanvasEvent(

self, title, item, event, function, add=None)

def setCanvasEvent(self, title, item, event, function, add=None):
    canvas = self.widgetManager.get(WIDGET_NAMES.Canvas, title)
    canvas.tag_bind(item, event, function, add)

def setCanvasMap(

self, name, func, coords)

def setCanvasMap(self, name, func, coords):
    self._setWidgetMap(name, WIDGET_NAMES.Canvas, func, coords)

def setCheckBox(

self, title, ticked=True, callFunction=True)

def setCheckBox(self, title, ticked=True, callFunction=True):
    cb = self.widgetManager.get(WIDGET_NAMES.CheckBox, title)
    bVar = self.widgetManager.get(WIDGET_NAMES.CheckBox, title, group=WidgetManager.VARS)
    bVar.set(ticked)
    if ticked:
        if not self.ttkFlag:
            cb.select()
        else:
            cb.state(['selected'])
    else:
        if not self.ttkFlag:
            cb.deselect()
        else:
            cb.state(['!selected'])
    # now call function
    if callFunction:
        if hasattr(cb, 'cmd'):
            cb.cmd()

def setCheckBoxBoxBg(

self, title, newCol)

def setCheckBoxBoxBg(self, title, newCol):
    self.setCheckBoxSelectColour(title, newCol)

def setCheckBoxSelectColour(

self, title, newCol)

def setCheckBoxSelectColour(self, title, newCol):
    cb = self.widgetManager.get(WIDGET_NAMES.CheckBox, title)
    cb.config(selectcolor=newCol)

def setCheckBoxText(

self, title, text)

def setCheckBoxText(self, title, text):
    cb = self.widgetManager.get(WIDGET_NAMES.CheckBox, title)
    cb.DEFAULT_TEXT = text
    cb.config(text=text)

def setColspan(

self, colspan)

def setColspan(self, colspan):
    self.containerStack[-1]['colspan'] = colspan

def setDatePicker(

self, title, date='today')

def setDatePicker(self, title, date="today"):
    self.widgetManager.get(WIDGET_NAMES.DatePicker, title)
    if date == "today":
        date = datetime.date.today()
    self.setOptionBox(title + "_DP_YearOptionBox", str(date.year))
    self.setOptionBox(title + "_DP_MonthOptionBox", date.month - 1)
    self.setOptionBox(title + "_DP_DayOptionBox", date.day - 1)

def setDatePickerChangeFunction(

self, title, function)

def setDatePickerChangeFunction(self, title, function):
    self.widgetManager.get(WIDGET_NAMES.DatePicker, title)
    cmd = self.MAKE_FUNC(function, title)
    self.setOptionBoxChangeFunction(title + "_DP_DayOptionBox", cmd)
    self.widgetManager.get(WIDGET_NAMES.OptionBox, title + "_DP_DayOptionBox").function = cmd

def setDatePickerFg(

self, name, fg)

def setDatePickerFg(self, name, fg):
    self.widgetManager.get(WIDGET_NAMES.DatePicker, name)
    self.setLabelFg(name + "_DP_DayLabel", fg)
    self.setLabelFg(name + "_DP_MonthLabel", fg)
    self.setLabelFg(name + "_DP_YearLabel", fg)

def setDatePickerRange(

self, title, startYear, endYear=None)

def setDatePickerRange(self, title, startYear, endYear=None):
    self.widgetManager.get(WIDGET_NAMES.DatePicker, title)
    if endYear is None:
        endYear = datetime.date.today().year
    years = range(startYear, endYear + 1)
    self.changeOptionBox(title + "_DP_YearOptionBox", years)

def setEntry(

self, name, text, callFunction=True)

def setEntry(self, name, text, callFunction=True):
    ent = self.widgetManager.get(WIDGET_NAMES.Entry, name)
    var = self.widgetManager.get(WIDGET_NAMES.Entry, name, group=WidgetManager.VARS)
    self._updateEntryDefault(name, mode="set")
    # now call function
    with PauseCallFunction(callFunction, var, False):
        if not ent.isNumeric or self._validateNumericEntry("1", None, text, None, "1", None, None, None):
            var.set(text)

def setEntryDefault(

self, name, text='default')

def setEntryDefault(self, name, text="default"):
    entry = self.widgetManager.get(WIDGET_NAMES.Entry, name)
    self.widgetManager.get(WIDGET_NAMES.Entry, name, group=WidgetManager.VARS)
    # remember current settings - to return to
    if not hasattr(entry, "oldJustify"):
        entry.oldJustify = entry.cget('justify')
    if not hasattr(entry, "oldFg"):
        if not self.ttkFlag:
            entry.oldFg = entry.cget('foreground')
        else:
            entry.oldFg = entry.cget("style")
    # configure default stuff
    entry.default = text
    entry.DEFAULT_TEXT = text
    # only show new text if empty
    self._updateEntryDefault(name, "out")
    # bind commands to show/remove the default
    if hasattr(entry, "defaultInEvent"):
        entry.unbind(entry.defaultInEvent)
        entry.unbind(entry.defaultOutEvent)
    in_command = self.MAKE_FUNC(self._entryIn, name)
    out_command = self.MAKE_FUNC(self._entryOut, name)
    entry.defaultInEvent = entry.bind("<FocusIn>", in_command, add="+")
    entry.defaultOutEvent = entry.bind("<FocusOut>", out_command, add="+")

def setEntryInvalid(

self, title)

def setEntryInvalid(self, title):
    self.setValidationEntry(title, "invalid")

def setEntryLowerCase(

self, name)

def setEntryLowerCase(self, name):
    var = self.widgetManager.get(WIDGET_NAMES.Entry, name, group=WidgetManager.VARS)
    if var.lc_id is not None:
        var.trace_vdelete('w', var.lc_id)
    var.lc_id = var.trace('w', self.MAKE_FUNC(self._lowerEntry, name))

def setEntryMaxLength(

self, name, length)

def setEntryMaxLength(self, name, length):
    var = self.widgetManager.get(WIDGET_NAMES.Entry, name, group=WidgetManager.VARS)
    var.maxLength = length
    if var.ml_id is not None:
        var.trace_vdelete('w', var.ml_id)
    var.ml_id = var.trace('w', self.MAKE_FUNC(self._limitEntry, name))

def setEntryUpperCase(

self, name)

def setEntryUpperCase(self, name):
    var = self.widgetManager.get(WIDGET_NAMES.Entry, name, group=WidgetManager.VARS)
    if var.uc_id is not None:
        var.trace_vdelete('w', var.uc_id)
    var.uc_id = var.trace('w', self.MAKE_FUNC(self._upperEntry, name))

def setEntryValid(

self, title)

def setEntryValid(self, title):
    self.setValidationEntry(title, "valid")

def setEntryWaitingValidation(

self, title)

def setEntryWaitingValidation(self, title):
    self.setValidationEntry(title, "wait")

def setExpand(

self, exp)

def setExpand(self, exp):
    if exp is None or exp.lower() == "none":
        self.containerStack[-1]['expand'] = "NONE"
    elif exp.lower() == "row":
        self.containerStack[-1]['expand'] = "ROW"
    elif exp.lower() == "column":
        self.containerStack[-1]['expand'] = "COLUMN"
    else:
        self.containerStack[-1]['expand'] = "ALL"

def setFastStop(

self, fast=True)

def setFastStop(self, fast=True):
    self._fastStop = fast

def setFg(

self, colour, override=False)

def setFg(self, colour, override=False):
    if not self.ttkFlag:
        self.containerStack[-1]['fg']=colour
        gui.SET_WIDGET_FG(self._getContainerProperty('container'), colour, override)
        for child in self._getContainerProperty('container').winfo_children():
            if not self._isWidgetContainer(child):
                gui.SET_WIDGET_FG(child, colour, override)
    else:
        gui.trace("In ttk mode - trying to set FG to %s", colour)
        self.ttkStyle.configure("TLabel", foreground=colour)
        self.ttkStyle.configure("TFrame", foreground=colour)

def setFocus(

self, name)

def setFocus(self, name):
    entry = self.widgetManager.get(WIDGET_NAMES.Entry, name)
    entry.focus_set()

def setFont(

self, *args, **kwargs)

def setFont(self, *args, **kwargs):
    self.setInputFont(*args, **kwargs)
    self.setLabelFont(*args, **kwargs)
    self.setButtonFont(*args, **kwargs)

def setFullscreen(

self, title=None)

sets the specified window to be fullscreen if no title, will set the main GUI

def setFullscreen(self, title=None):
    """ sets the specified window to be fullscreen
        if no title, will set the main GUI """
    try:
        container = self.widgetManager.get(WIDGET_NAMES.SubWindow, title)
    except:
        container = self._getTopLevel()
    if not container.isFullscreen:
        container.isFullscreen = True
        container.attributes('-fullscreen', True)
        container.escapeBindId = container.bind('<Escape>', self.MAKE_FUNC(self.exitFullscreen, container), "+")

def setGoogleMapLocation(

self, title, location)

def setGoogleMapLocation(self, title, location):
    self.searchGoogleMap(title, location)

def setGoogleMapMarker(

self, title, location, size=None, colour=None, label=None, replace=False)

def setGoogleMapMarker(self, title, location, size=None, colour=None, label=None, replace=False):
    gMap = self.widgetManager.get(WIDGET_NAMES.Map, title)
    if len(location) == 0:
        gMap.removeMarkers()
    else:
        gMap.addMarker(location, size, colour, label, replace)

def setGoogleMapProxy(

self, title, proxyString)

def setGoogleMapProxy(self, title, proxyString):
    gMap = self.widgetManager.get(WIDGET_NAMES.Map, title)
    gMap.setProxyString(proxyString)

def setGoogleMapSize(

self, title, size)

def setGoogleMapSize(self, title, size):
    gMap = self.widgetManager.get(WIDGET_NAMES.Map, title)
    gMap.setSize(size)

def setGoogleMapTerrain(

self, title, terrain)

def setGoogleMapTerrain(self, title, terrain):
    gMap = self.widgetManager.get(WIDGET_NAMES.Map, title)
    if terrain not in gMap.TERRAINS:
        raise Exception("Invalid terrain. Must be one of " + str(gMap.TERRAINS))
    gMap.changeTerrain(terrain)

def setGoogleMapZoom(

self, title, mod)

def setGoogleMapZoom(self, title, mod):
    self. zoomGoogleMap(title, mod)

def setGridHeaders(

self, title, data)

def setGridHeaders(self, title, data):
    return self.setTableHeaders(title, data)

def setGuiPadding(

self, x, y=None)

sets the padding around the border of the GUI

def setGuiPadding(self, x, y=None):
    """ sets the padding around the border of the GUI """
    x, y = gui.PARSE_TWO_PARAMS(x, y)
    self.containerStack[0]['container'].config(padx=x, pady=y)

def setIPadX(

self, x=0)

def setIPadX(self, x=0):
    self.setInPadX(x)

def setIPadY(

self, y=0)

def setIPadY(self, y=0):
    self.setInPadY(y)

def setIPadding(

self, x, y=None)

def setIPadding(self, x, y=None):
    self.setInPadding(x, y)

def setIcon(

self, image)

def setIcon(self, image):
    container = self._getTopLevel()
    container.winIcon = image
    if image.endswith('.ico'):
        container.wm_iconbitmap(image)
    else:
        icon = self._getImage(image)
        container.iconphoto(True, icon)

def setImage(

self, name, imageFile, internal=False)

def setImage(self, name, imageFile, internal=False):
    label = self.widgetManager.get(WIDGET_NAMES.Image, name)
    imageFile = self.getImagePath(imageFile)
    # only set the image if it's different
    if label.image is not None and label.image.path == imageFile:
        self.warn("Not updating %s, %s hasn't changed." , name, imageFile)
        return
    elif imageFile is None:
        return
    else:
        image = self._getImage(imageFile)
        self._populateImage(name, image, internal)

def setImageData(

self, name, imageData, fmt='gif')

def setImageData(self, name, imageData, fmt="gif"):
    label = self.widgetManager.get(WIDGET_NAMES.Image, name)
    image = self._getImageData(imageData, fmt=fmt)
    self._populateImage(name, image)

def setImageLocation(

self, location)

def setImageLocation(self, location):
    if os.path.isdir(location):
        self.userImages = location
    else:
        raise Exception("Invalid image location: " + location)

def setImageMap(

self, name, func, coords)

def setImageMap(self, name, func, coords):
    self._setWidgetMap(name, WIDGET_NAMES.Image, func, coords)

def setImageMouseOver(

self, title, overImg)

def setImageMouseOver(self, title, overImg):
    lab = self.widgetManager.get(WIDGET_NAMES.Image, title)
    # first check over image & cache it
    fullPath = self.getImagePath(overImg)
    self.topLevel.after(0, self._getImage, fullPath)
    leaveImg = lab.image.path
    lab.bind("<Leave>", lambda e: self.setImage(title, leaveImg, True))
    lab.bind("<Enter>", lambda e: self.setImage(title, fullPath, True))
    lab.hasMouseOver = True

def setImageSize(

self, name, width, height)

def setImageSize(self, name, width, height):
    img = self.widgetManager.get(WIDGET_NAMES.Image, name)
    img.config(height=height, width=width)

def setInPadX(

self, x=0)

def setInPadX(self, x=0):
    self.containerStack[-1]['ipadx'] = x

def setInPadY(

self, y=0)

def setInPadY(self, y=0):
    self.containerStack[-1]['ipady'] = y

def setInPadding(

self, x, y=None)

def setInPadding(self, x, y=None):
    x, y = gui.PARSE_TWO_PARAMS(x, y)
    self.containerStack[-1]['ipadx'] = x
    self.containerStack[-1]['ipady'] = y

def setInputFont(

self, *args, **kwargs)

def setInputFont(self, *args, **kwargs):
    self._fontHelper('inputFont', *args, **kwargs)

def setLabel(

self, name, text)

def setLabel(self, name, text):
    lab = self.widgetManager.get(WIDGET_NAMES.Label, name)
    lab.config(text=text)

def setLabelFont(

self, *args, **kwargs)

def setLabelFont(self, *args, **kwargs):
    kwargs = self._fontHelper('labelFont', *args, **kwargs)
    if kwargs is not None:
        self.tableFont.config(**kwargs)
        # need better way to register font change events on tables
        for k, v in self.widgetManager.group(WIDGET_NAMES.Table).items():
            v.config(font=self.tableFont)
        linkArgs = kwargs.copy()
        linkArgs['underline'] = True
        linkArgs['weight'] = 'bold'
        self._linkFont.config(**linkArgs)

def setLabelFrameTitle(

self, title, newTitle)

def setLabelFrameTitle(self, title, newTitle):
    frame = self.widgetManager.get(WIDGET_NAMES.LabelFrame, title)
    frame.config(text=newTitle)

def setLanguage(

self, language)

wrapper for changeLanguage()

def setLanguage(self, language):
    """ wrapper for changeLanguage() """
    self.changeLanguage(language)

def setListBoxGroup(

self, name, group=True)

def setListBoxGroup(self, name, group=True):
    lb = self.widgetManager.get(WIDGET_NAMES.ListBox, name)
    group = not group
    lb.config(exportselection=group)

def setListBoxMulti(

self, title, multi=True)

def setListBoxMulti(self, title, multi=True):
    lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title)
    if multi:
        lb.config(selectmode=EXTENDED)
    else:
        lb.config(selectmode=BROWSE)

def setListBoxRows(

self, name, rows)

def setListBoxRows(self, name, rows):
    lb = self.widgetManager.get(WIDGET_NAMES.ListBox, name)
    lb.config(height=rows)

def setListItem(

self, title, item, newVal, first=False)

def setListItem(self, title, item, newVal, first=False):
    for pos in self._getListPositions(title, item):
        self.setListItemAtPos(title, pos, newVal)
        if first:
            break

def setListItemAtPos(

self, title, pos, newVal)

def setListItemAtPos(self, title, pos, newVal):
    lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title)
    lb.delete(pos)
    lb.insert(pos, newVal)

def setListItemAtPosBg(

self, title, pos, col)

def setListItemAtPosBg(self, title, pos, col):
    lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title)
    lb.itemconfig(pos, bg=col)

def setListItemAtPosFg(

self, title, pos, col)

def setListItemAtPosFg(self, title, pos, col):
    lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title)
    lb.itemconfig(pos, fg=col)

def setListItemBg(

self, title, item, col)

def setListItemBg(self, title, item, col):
    for pos in self._getListPositions(title, item):
        self.setListItemAtPosBg(title, pos, col)

def setListItemFg(

self, title, item, col)

def setListItemFg(self, title, item, col):
    for pos in self._getListPositions(title, item):
        self.setListItemAtPosFg(title, pos, col)

def setLocation(

self, x, y=None, ignoreSettings=None, win=None, up=0)

called to set the GUI's position on screen

def setLocation(self, x, y=None, ignoreSettings=None, win=None, up=0):
    """ called to set the GUI's position on screen """
    if win is None:
        win = self._getTopLevel()
    gui.SET_LOCATION(x, y, ignoreSettings, win, up)

def setLogFile(

fileName)

sets the filename for logging messages

@staticmethod
def setLogFile(fileName):
    """ sets the filename for logging messages """
    # Remove all handlers associated with the root logger object.
    for handler in logging.root.handlers[:]:
        logging.root.removeHandler(handler)
    logging.basicConfig(level=logging.INFO, filename=fileName, format='%(asctime)s %(name)s:%(levelname)s: %(message)s')
    gui.info("Switched to logFile: %s", fileName)

def setLogLevel(

level)

main function for setting the logging level provide one of: INFO, DEBUG, WARNING, ERROR, CRITICAL, EXCEPTION, None

@staticmethod
def setLogLevel(level):
    """ main function for setting the logging level
        provide one of: INFO, DEBUG, WARNING, ERROR, CRITICAL, EXCEPTION, None """
    logging.getLogger("appJar").setLevel(getattr(logging, level.upper()))
    gui.info("Log level changed to: %s", level)

def setMenuCheckBox(

self, menu, name, value=None)

def setMenuCheckBox(self, menu, name, value=None):
    self._setMenu(menu, name, value, "cb")

def setMenuIcon(

self, menu, title, icon, align='left')

def setMenuIcon(self, menu, title, icon, align="left"):
    image = os.path.join(self.icon_path, icon.lower() + ".png")
    with PauseLogger():
        self.setMenuImage(menu, title, image, align)

def setMenuImage(

self, menu, title, image, align='left')

def setMenuImage(self, menu, title, image, align="left"):
    theMenu = self.widgetManager.get(WIDGET_NAMES.Menu, menu)
    imageObj = self._getImage(image)
    if 16 != imageObj.width() or imageObj.width() != imageObj.height():
        self.warn("Invalid image resolution for menu item %s (%s) - should be 16x16", title, image)
        #imageObj = imageObj.subsample(2,2)
    try: theMenu.entryconfigure(title, image=imageObj, compound=align)
    except TclError: gui.error("Unable to set image for menu item: %s, in menu: %s - item not found", title, menu)

def setMenuRadioButton(

self, menu, name, value)

def setMenuRadioButton(self, menu, name, value):
    self._setMenu(menu, name, value, "rb")

def setMessage(

self, title, text)

def setMessage(self, title, text):
    mess = self.widgetManager.get(WIDGET_NAMES.Message, title)
    mess.config(text=text)

def setMessageAspect(

self, title, aspect)

set a new aspect ratio for the text in this widget

def setMessageAspect(self, title, aspect):
    """ set a new aspect ratio for the text in this widget """
    mess = self.widgetManager.get(WIDGET_NAMES.Message, title)
    mess.config(aspect=aspect)

def setMeter(

self, name, value=0.0, text=None)

def setMeter(self, name, value=0.0, text=None):
    item = self.widgetManager.get(WIDGET_NAMES.Meter, name)
    item.set(value, text)

def setMeterFill(

self, name, colour)

def setMeterFill(self, name, colour):
    item = self.widgetManager.get(WIDGET_NAMES.Meter, name)
    item.configure(fill=colour)

def setMicroBitImage(

self, title, image)

def setMicroBitImage(self, title, image):
    self.widgetManager.get(WIDGET_NAMES.MicroBit, title).show(image)

def setMicroBitPixel(

self, title, x, y, brightness)

def setMicroBitPixel(self, title, x, y, brightness):
    self.widgetManager.get(WIDGET_NAMES.MicroBit, title).set_pixel(x, y, brightness)

def setMinSize(

self, container=None, size=None)

sets a minimum size for the specified container - defaults to the whole GUI

def setMinSize(self, container=None, size=None):
    """ sets a minimum size for the specified container - defaults to the whole GUI """
    if container is None: container = self.topLevel
    if size is None: size = (gui.GET_DIMS(container)["r_width"], gui.GET_DIMS(container)["r_height"])
    container.ms = size
    container.minsize(size[0], size[1])
    gui.trace("Minsize set to: %s", size)

def setOnTop(

self, stay=True)

def setOnTop(self, stay=True):
    self._getTopLevel().attributes("-topmost", stay)
    gui.trace("Staying on top set to: %s", stay)

def setOptionBox(

self, title, index, value=True, callFunction=True, override=False)

Main purpose is to select/deselect the item at the specified position But will also: delete an item if value is set to None or rename an item if value is set to a String

:param title: the OptionBox to change :param index: the position or value of the item to select/delete :param value: determines what to do to the item: if set to None, will delete the item, else it sets the items state :param callFunction: whether to generate an event to notify that the widget has changed :param override: if set to True, allows a disabled item to be selected :returns: None :raises ItemLookupError: if the title can't be found

def setOptionBox(self, title, index, value=True, callFunction=True, override=False):
    """ Main purpose is to select/deselect the item at the specified position
    But will also: delete an item if value is set to None or rename an item if value is set to a String
    :param title: the OptionBox to change
    :param index: the position or value of the item to select/delete
    :param value: determines what to do to the item: if set to None, will delete the item, else it sets the items state
    :param callFunction: whether to generate an event to notify that the widget has changed
    :param override: if set to True, allows a disabled item to be selected
    :returns: None
    :raises ItemLookupError: if the title can't be found
    """
    box = self.widgetManager.get(WIDGET_NAMES.OptionBox, title)
    if box.kind == "ticks":
        gui.trace("Updating tickOptionBox")
        ticks = self.widgetManager.get(WIDGET_NAMES.TickOptionBox, title, group=WidgetManager.VARS)
        if index is None:
            gui.trace("Index empty - nothing to update")
            return
        elif index in ticks:
            gui.trace("Updating: %s", index)
            tick = ticks[index]
            try:
                index_num = box.options.index(index)
            except:
                self.warn("Unknown tick: %s in OptionBox: %s", index, title)
                return
            with PauseCallFunction(callFunction, tick, useVar=False):
                if value is None: # then we need to delete it
                    gui.trace("Deleting tick: %s from OptionBox %s", index, title)
                    box['menu'].delete(index_num)
                    del(box.options[index_num])
                    self.widgetManager.remove(WIDGET_NAMES.TickOptionBox, title, index, group=WidgetManager.VARS)
                elif isinstance(value, bool):
                    gui.trace("Updating tick: %s from OptionBox: %s to: %s", index, title, value)
                    tick.set(value)
                else:
                    gui.trace("Renaming tick: %s from OptionBox: %s to: %s", index, title, value)
                    ticks = self.widgetManager.get(WIDGET_NAMES.TickOptionBox, title, group=WidgetManager.VARS)
                    ticks[value] = ticks.pop(index)
                    box.options[index_num] = value
                    self.changeOptionBox(title, box.options)
                    for tick in ticks:
                        self.widgetManager.get(WIDGET_NAMES.TickOptionBox, title, group=WidgetManager.VARS)[tick].set(ticks[tick].get())
        else:
            if value is None:
                self.warn("Unknown tick in deleteOptionBox: %s in OptionBox: %s" , index, title)
            else:
                self.warn("Unknown tick in setOptionBox: %s in OptionBox: %s", index, title)
    else:
        gui.trace("Updating regular optionBox: %s at: %s to: %s", title, index, value)
        count = len(box.options)
        if count > 0:
            if index is None:
                index = 0
            if not isinstance(index, int):
                try:
                    index = box.options.index(index)
                except:
                    if value is None:
                        self.warn("Unknown option in deleteOptionBox: %s in OptionBox: %s", index, title)
                    else:
                        self.warn("Unknown option in setOptionBox: %s in OptionBox: %s", index, title)
                    return
            gui.trace("--> index now: %s", index)
            if index < 0 or index > count - 1:
                self.warn("Invalid option: %s. Should be between 0 and %s." , count-1, index)
            else:
                if value is None: # then we can delete it...
                    gui.trace("Deleting option: %s from OptionBox: %s", index, title)
                    box['menu'].delete(index)
                    del(box.options[index])
                    self.setOptionBox(title, 0, callFunction=False, override=override)
                elif isinstance(value, bool):
                    gui.trace("Updating: OptionBox: %s to: %s", title, index)
                    with PauseCallFunction(callFunction, box):
                        if not box['menu'].invoke(index):
                            if override:
                                gui.trace("Setting OptionBox: %s to disabled option: %s", title, index)
                                box["menu"].entryconfigure(index, state="normal")
                                box['menu'].invoke(index)
                                box["menu"].entryconfigure(index, state="disabled")
                            else:
                                self.warn("Unable to set disabled option: %s in OptionBox %s. Try setting 'override=True'", index, title)
                        else:
                            gui.trace("Invoked item: %s", index)
                else:
                    gui.trace("Renaming: %s from OptionBox: %s to: %s", index, title, value)
                    pos = box.options.index(self.widgetManager.get(WIDGET_NAMES.OptionBox, title, group=WidgetManager.VARS).get())
                    box.options[index] = value
                    self.changeOptionBox(title, box.options, pos)
        else:
            self.widgetManager.get(WIDGET_NAMES.OptionBox, title, group=WidgetManager.VARS).set("")
            self.warn("No items to select from: %s", title)

def setOptionBoxDisabledChar(

self, title, disabled='-')

def setOptionBoxDisabledChar(self, title, disabled="-"):
    box = self.widgetManager.get(WIDGET_NAMES.OptionBox, title)
    box.disabled = disabled
    self._disableOptionBoxSeparators(box)

def setPadX(

self, x=0)

set the current container's external grid padding

def setPadX(self, x=0):
    """ set the current container's external grid padding """
    self.containerStack[-1]['padx'] = x

def setPadY(

self, y=0)

set the current container's external grid padding

def setPadY(self, y=0):
    """ set the current container's external grid padding """
    self.containerStack[-1]['pady'] = y

def setPadding(

self, x, y=None)

sets the padding around the border of the current container

def setPadding(self, x, y=None):
    """ sets the padding around the border of the current container """
    x, y = gui.PARSE_TWO_PARAMS(x, y)
    self.containerStack[-1]['padx'] = x
    self.containerStack[-1]['pady'] = y

def setPagedWindowButtons(

self, title, buttons)

def setPagedWindowButtons(self, title, buttons):
    pager = self.widgetManager.get(WIDGET_NAMES.PagedWindow, title)
    if not isinstance(buttons, list) or len(buttons) != 2:
        raise Exception(
            "You must provide a list of two strings for setPagedWinowButtons()")
    pager.setPrevButton(buttons[0])
    pager.setNextButton(buttons[1])

def setPagedWindowButtonsTop(

self, title, top=True)

def setPagedWindowButtonsTop(self, title, top=True):
    pager = self.widgetManager.get(WIDGET_NAMES.PagedWindow, title)
    pager.setNavPositionTop(top)

def setPagedWindowFunction(

self, title, func)

def setPagedWindowFunction(self, title, func):
    pager = self.widgetManager.get(WIDGET_NAMES.PagedWindow, title)
    command = self.MAKE_FUNC(func, title)
    pager.registerPageChangeEvent(command)

def setPagedWindowPage(

self, title, page)

def setPagedWindowPage(self, title, page):
    pager = self.widgetManager.get(WIDGET_NAMES.PagedWindow, title)
    pager.showPage(page)

def setPagedWindowTitle(

self, title, pageTitle)

def setPagedWindowTitle(self, title, pageTitle):
    pager = self.widgetManager.get(WIDGET_NAMES.PagedWindow, title)
    pager.setTitle(pageTitle)

def setPaneSashPosition(

self, pos, pane=None)

def setPaneSashPosition(self, pos, pane=None):
    # convert to a percentage if needed
    if pos > 1: pos = pos / 100.0
    if pane is None:
        if self._getContainerProperty('type') == WIDGET_NAMES.PanedFrame:
            pane = self._getContainerProperty('container')
        elif self.containerStack[-2]['type'] == WIDGET_NAMES.PanedFrame:
            pane = self.containerStack[-2]['container']
        elif self._getContainerProperty('type') == WIDGET_NAMES.Pane:
            pane = self._getContainerProperty('container').parent
        else:
            gui.error("Unable to set sash position - can't find a pane")
            return
    elif type(pane) == str:
        pane = self.widgetManager.get(WIDGET_NAMES.PanedFrame, pane)
    if pane.cget('orient') == 'horizontal':
        w = int(pane.winfo_width() * pos)
        try:
            pane.sash_place(0, w, 0)
            gui.trace('Set horizontal pane: %s to position: %s', pane, pos)
        except TclError as e:
            # no sash to configure - last pane
            pass
    else:
        h = int(pane.winfo_height() * pos)
        try:
            pane.sash_place(0, 0, h)
            gui.trace('Set vertical pane: %s to position: %s', pane, pos)
        except TclError as e:
            # no sash to configure - last pane
            pass

def setPanedFrameVertical(

self, window)

def setPanedFrameVertical(self, window):
    pane = self.widgetManager.get(WIDGET_NAMES.PanedFrame, window)
    pane.config(orient=VERTICAL)

def setPieChart(

self, title, name, value)

def setPieChart(self, title, name, value):
    pie = self.widgetManager.get(WIDGET_NAMES.PieChart, title)
    pie.setValue(name, value)

def setPollTime(

self, time)

Set a frequency for executing queued functions events will fire in order of being added, after sleeping for time

def setPollTime(self, time):
    """ Set a frequency for executing queued functions
        events will fire in order of being added, after sleeping for time """
    self.pollTime = time

def setProperties(

self, title, props, callFunction=True)

def setProperties(self, title, props, callFunction=True):
    p = self.widgetManager.get(WIDGET_NAMES.Properties, title)
    p.addProperties(props, callFunction=callFunction)

def setPropertiesBoxBg(

self, title, newCol)

def setPropertiesBoxBg(self, title, newCol):
    self.setPropertiesSelectColour(title, newCol)

def setPropertiesSelectColour(

self, title, newCol)

def setPropertiesSelectColour(self, title, newCol):
    props = self.widgetManager.get(WIDGET_NAMES.Properties, title)
    props.config(selectcolor=newCol)

def setProperty(

self, title, prop, value=False, callFunction=True)

def setProperty(self, title, prop, value=False, callFunction=True):
    props = self.widgetManager.get(WIDGET_NAMES.Properties, title)
    props.addProperty(prop, value, callFunction=callFunction)

def setPropertyText(

self, title, prop, newText=None)

def setPropertyText(self, title, prop, newText=None):
    props = self.widgetManager.get(WIDGET_NAMES.Properties, title)
    props.renameProperty(prop, newText)

def setRadioButton(

self, title, value, callFunction=True)

def setRadioButton(self, title, value, callFunction=True):
    ident = title + "-" + value
    self.widgetManager.get(WIDGET_NAMES.RadioButton, ident)
    # now call function
    var = self.widgetManager.get(WIDGET_NAMES.RadioButton, title, group=WidgetManager.VARS)
    with PauseCallFunction(callFunction, var, False):
        var.set(value)

def setRadioSquare(

self, title, square=True)

def setRadioSquare(self, title, square=True):
    if self.platform == self.MAC:
        gui.warn("Square radiobuttons not available on Mac, for radiobutton %s", title)
    elif not self.ttkFlag:
        for k, v in self.widgetManager.group(WIDGET_NAMES.RadioButton).items():
            if k.startswith(title+"-"):
                if square:
                    v.config(indicatoron=1)
                else:
                    v.config(indicatoron=0)
    else:
        gui.warn("Square radiobuttons not available in ttk mode, for radiobutton %s", title)

def setRadioTick(

self, title, tick=True)

def setRadioTick(self, title, tick=True):
    self.warn("Deprecated function (%s) used for %s -> %s use %s instead", 'setRadioTick', 'radioButton', title, 'setRadioSquare')
    self.setRadioSquare(title, square=tick)

def setResizable(

self, canResize=True)

def setResizable(self, canResize=True):
    self._getTopLevel().isResizable = canResize
    if self._getTopLevel().isResizable:
        self._getTopLevel().resizable(True, True)
    else:
        self._getTopLevel().resizable(False, False)

def setRow(

self, row)

def setRow(self, row):
    self.containerStack[-1]['emptyRow'] = row

def setRowspan(

self, rowspan)

def setRowspan(self, rowspan):
    self.containerStack[-1]['rowspan'] = rowspan

def setScale(

self, title, pos, callFunction=True)

def setScale(self, title, pos, callFunction=True):
    sc = self.widgetManager.get(WIDGET_NAMES.Scale, title)
    with PauseCallFunction(callFunction, sc):
        sc.set(pos)

def setScaleHorizontal(

self, title)

def setScaleHorizontal(self, title):
    sc = self.widgetManager.get(WIDGET_NAMES.Scale, title)
    sc.config(orient=HORIZONTAL)

def setScaleIncrement(

self, title, increment)

def setScaleIncrement(self, title, increment):
    sc = self.widgetManager.get(WIDGET_NAMES.Scale, title)
    sc.increment = increment

def setScaleLength(

self, title, length)

def setScaleLength(self, title, length):
    if not self.ttkFlag:
        sc = self.widgetManager.get(WIDGET_NAMES.Scale, title)
        sc.config(sliderlength=length)
    else:
        self.warn("ttk: setScaleLength() not supported: %s", title)

def setScaleRange(

self, title, start, end, curr=None)

def setScaleRange(self, title, start, end, curr=None):
    if curr is None:
        curr = start
    sc = self.widgetManager.get(WIDGET_NAMES.Scale, title)
    sc.config(from_=start, to=end)
    self.setScale(title, curr)
    # set the increment as 10%
    try:
        res = sc.cget("resolution")
        diff = int((((end - start)/res)/10)+0.99) # add 0.99 to round up...
        sc.increment = diff
    except:
        pass # resolution not supported in ttk

def setScaleVertical(

self, title)

def setScaleVertical(self, title):
    sc = self.widgetManager.get(WIDGET_NAMES.Scale, title)
    sc.config(orient=VERTICAL)

def setSetting(

self, name, value)

adds a setting to the settings file

def setSetting(self, name, value):
    """ adds a setting to the settings file """
    self.externalSettings[name] = value

def setSize(

self, geom, height=None, ignoreSettings=None)

called to update screen geometry can take a geom string, or a width & height can override ignoreSettings if desired

def setSize(self, geom, height=None, ignoreSettings=None):
    """ called to update screen geometry
        can take a geom string, or a width & height
        can override ignoreSettings if desired """
    container = self._getTopLevel()
    if ignoreSettings is not None:
        container.ignoreSettings = ignoreSettings
    try:
        geom = geom.lower()
    except:
        # ignore - other data types allowed
        pass
    if geom == "fullscreen":
        self.setFullscreen()
    elif geom is not None:
        if height is not None:
            geom=(geom, height)
        elif not isinstance(geom, list) and not isinstance(geom, tuple):
            geom, loc = gui.SPLIT_GEOM(geom)
        size = "%sx%s" % (int(geom[0]), int(geom[1]))
        gui.trace("Setting size: %s", size)
        # warn the user that their geom is not big enough
        dims = gui.GET_DIMS(container)
        if geom[0] < dims["b_width"] or geom[1] < dims["b_height"]:
            self.trace("Specified dimensions (%s, %s) less than requested dimensions (%s, %s)",
                    geom[0], geom[1], dims["b_width"], dims["b_height"])
        # and set it as the minimum size
        if not hasattr(container, 'ms'):
            self.setMinSize(container, geom)
        self.exitFullscreen()
        container.geometry(size)

def setSoundLocation(

self, location)

def setSoundLocation(self, location):
    if os.path.isdir(location):
        self.userSounds = location
    else:
        raise Exception("Invalid sound location: " + location)

def setSpinBox(

self, title, value, callFunction=True)

def setSpinBox(self, title, value, callFunction=True):
    spin = self.widgetManager.get(WIDGET_NAMES.SpinBox, title)
    vals = spin.cget("values")  # .split()
    vals = self._getSpinBoxValsAsList(vals)
    val = str(value)
    if val not in vals:
        raise Exception( "Invalid value: " + val + ". Not in SpinBox: " +
                    title + "=" + str(vals))
    self._setSpinBoxVal(spin, val, callFunction)

def setSpinBoxPos(

self, title, pos, callFunction=True)

def setSpinBoxPos(self, title, pos, callFunction=True):
    spin = self.widgetManager.get(WIDGET_NAMES.SpinBox, title)
    vals = spin.cget("values")  # .split()
    vals = self._getSpinBoxValsAsList(vals)
    pos = int(pos)
    if pos < 0 or pos >= len(vals):
        raise Exception( "Invalid position: " + str(pos) + ". No position in SpinBox: " +
                    title + "=" + str(vals))
    pos = len(vals) - 1 - pos
    val = vals[pos]
    self._setSpinBoxVal(spin, val, callFunction)

def setStartFrame(

self, title, num)

def setStartFrame(self, title, num):
    self.widgetManager.get(WIDGET_NAMES.FrameStack, title).setStartFrame(num)

def setStartFunction(

self, func)

def setStartFunction(self, func):
    f = self.MAKE_FUNC(func, "start")
    self.topLevel.startFunction = f

def setStatusFont(

self, *args, **kwargs)

def setStatusFont(self, *args, **kwargs):
    self._fontHelper('statusFont', *args, **kwargs)

def setStatusbar(

self, text, field=0)

def setStatusbar(self, text, field=0):
    if self.hasStatus:
        if field is None:
            for status in self._statusFields:
                status.config(text=self._getFormatStatus(text))
        elif field >= 0 and field < len(self._statusFields):
            self._statusFields[field].config(text=self._getFormatStatus(text))
        else:
            raise Exception("Invalid status field: " + str(field) +
                            ". Must be between 0 and " + str(len(self._statusFields) - 1))

def setStatusbarBg(

self, colour, field=None)

def setStatusbarBg(self, colour, field=None):
    if self.hasStatus:
        if field is None:
            for status in self._statusFields:
                status.config(background=colour)
        elif field >= 0 and field < len(self._statusFields):
            self._statusFields[field].config(background=colour)
        else:
            raise Exception("Invalid status field: " + str(field) +
                            ". Must be between 0 and " + str(len(self._statusFields) - 1))

def setStatusbarFg(

self, colour, field=None)

def setStatusbarFg(self, colour, field=None):
    if self.hasStatus:
        if field is None:
            for status in self._statusFields:
                status.config(foreground=colour)
        elif field >= 0 and field < len(self._statusFields):
            self._statusFields[field].config(foreground=colour)
        else:
            raise Exception("Invalid status field: " + str(field) +
                            ". Must be between 0 and " + str(len(self._statusFields) - 1))

def setStatusbarHeader(

self, header)

def setStatusbarHeader(self, header):
    if self.hasStatus:
        self.header = header

def setStatusbarWidth(

self, width, field=None)

def setStatusbarWidth(self, width, field=None):
    if self.hasStatus:
        if field is None:
            for status in self._statusFields:
                status.config(width=width)
        elif field >= 0 and field < len(self._statusFields):
            self._statusFields[field].config(width=width)
        else:
            raise Exception("Invalid status field: " + str(field) +
                            ". Must be between 0 and " + str(len(self._statusFields) - 1))

def setSticky(

self, sticky)

def setSticky(self, sticky):
    self.containerStack[-1]['sticky'] = sticky

def setStopFunction(

self, function)

Set a function to call when the GUI is quit. Must return True or False

def setStopFunction(self, function):
    """ Set a function to call when the GUI is quit. Must return True or False """
    tl = self._getTopLevel()
    tl.stopFunction = function
    # link to exit item in topMenu
    # only if in root
    if self._getContainerProperty('type') != WIDGET_NAMES.SubWindow:
        tl.createcommand('exit', self.stop)

def setStretch(

self, exp)

def setStretch(self, exp):
    self.setExpand(exp)

def setSubWindowLocation(

self, title, x, y)

def setSubWindowLocation(self, title, x, y):
    self.widgetManager.get(WIDGET_NAMES.SubWindow, title).setLocation(x, y)

def setTabBg(

self, title, tab, colour)

def setTabBg(self, title, tab, colour):
    nb = self.widgetManager.get(WIDGET_NAMES.TabbedFrame, title)
    tab = nb.getTab(tab)
    gui.SET_WIDGET_BG(tab, colour)
    # tab.config(bg=colour)
    #gui.SET_WIDGET_BG(tab, colour)
    for child in tab.winfo_children():
        gui.SET_WIDGET_BG(child, colour)

def setTabFont(

self, title, **kwargs)

def setTabFont(self, title, **kwargs):
    nb = self.widgetManager.get(WIDGET_NAMES.TabbedFrame, title)
    nb.setFont(**kwargs)

def setTabText(

self, title, tab, newText=None)

def setTabText(self, title, tab, newText=None):
    nb = self.widgetManager.get(WIDGET_NAMES.TabbedFrame, title)
    nb.renameTab(tab, newText)

def setTabbedFrameChangeCommand(

self, title, func)

def setTabbedFrameChangeCommand(self, title, func):
    nb = self.widgetManager.get(WIDGET_NAMES.TabbedFrame, title)
    command = self.MAKE_FUNC(func, title)
    nb.config(command=command)

def setTabbedFrameDisableAllTabs(

self, title, disabled=True)

def setTabbedFrameDisableAllTabs(self, title, disabled=True):
    nb = self.widgetManager.get(WIDGET_NAMES.TabbedFrame, title)
    nb.disableAllTabs(disabled)

def setTabbedFrameDisabledTab(

self, title, tab, disabled=True)

def setTabbedFrameDisabledTab(self, title, tab, disabled=True):
    nb = self.widgetManager.get(WIDGET_NAMES.TabbedFrame, title)
    nb.disableTab(tab, disabled)

def setTabbedFrameSelectedTab(

self, title, tab, callFunction=True)

def setTabbedFrameSelectedTab(self, title, tab, callFunction=True):
    nb = self.widgetManager.get(WIDGET_NAMES.TabbedFrame, title)
    try:
        nb.changeTab(tab, callFunction)
    except KeyError:
        raise ItemLookupError("Invalid tab name: " + str(tab))

def setTabbedFrameTabExpand(

self, title, expand=True)

def setTabbedFrameTabExpand(self, title, expand=True):
    nb = self.widgetManager.get(WIDGET_NAMES.TabbedFrame, title)
    nb.expandTabs(expand)

def setTableEditFunction(

self, title, func)

def setTableEditFunction(self, title, func):
    grid = self.widgetManager.get(WIDGET_NAMES.Table, title)
    cmd = self.MAKE_FUNC(func, title)
    grid.config(edit=cmd)

def setTableHeaders(

self, title, data)

change the headers in the specified table

def setTableHeaders(self, title, data):
    ''' change the headers in the specified table '''
    grid = self.widgetManager.get(WIDGET_NAMES.Table, title)
    grid.setHeaders(data)

def setTextArea(

self, title, text, end=True, callFunction=True, tag=None)

Add the supplied text to the specified TextArea

:param title: the TextArea to change :param text: the text to add to the TextArea :param end: where to insert the text, by default it is added to the end. Set end to False to add to the beginning. :param callFunction: whether to generate an event to notify that the widget has changed :returns: None :raises ItemLookupError: if the title can't be found

def setTextArea(self, title, text, end=True, callFunction=True, tag=None):
    """ Add the supplied text to the specified TextArea
    :param title: the TextArea to change
    :param text: the text to add to the TextArea
    :param end: where to insert the text, by default it is added to the end. Set end to False to add to the beginning.
    :param callFunction: whether to generate an event to notify that the widget has changed
    :returns: None
    :raises ItemLookupError: if the title can't be found
    """
    ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title)
    ta.pauseCallFunction(callFunction)
    # in case it's disabled
    _state = ta.cget('state')
    ta.config(state='normal')
    if end:
        pos = ta.index('end -1c linestart')
        ta.insert(END, text)
        ta.see(END)
         if tag is not None: self.textAreaTagRange(title, tag, pos)
    else:
        ta.insert('1.0', text)
        ta.see('1.0')
         if tag is not None: ta.textAreaTagPattern(title, tag, text)
    ta.config(state=_state)
    ta.resumeCallFunction()

def setTextAreaFont(

self, title, **kwargs)

changes the font of a text area

def setTextAreaFont(self, title, **kwargs):
    """ changes the font of a text area """
    self.widgetManager.get(WIDGET_NAMES.TextArea, title).setFont(**kwargs)

def setTitle(

self, title)

def setTitle(self, title):
    self._getTopLevel().title(title)

def setToggleFrameText(

self, title, newText)

def setToggleFrameText(self, title, newText):
    toggle = self.widgetManager.get(WIDGET_NAMES.ToggleFrame, title)
    toggle.config(text=newText)

def setToolbarBg(

self, bg)

def setToolbarBg(self, bg):
    self.tb.BG_COLOR = bg
    if not self.ttkFlag:
        self.tb.config(bg=self.tb.BG_COLOR)
        if gui.GET_PLATFORM() == gui.MAC:
            for name, val in self.widgetManager.group(WIDGET_NAMES.Toolbar).items():
                val.config(highlightbackground=self.tb.BG_COLOR)
        # config the pin button if exists
        if self.tb.pinBut is not None:
            self.tb.pinBut.config(bg=self.tb.BG_COLOR)
    else:
        self.ttkStyle.configure("Toolbar.TFrame", background=self.tb.BG_COLOR)
        self.ttkStyle.configure("Toolbar.TLabel", background=self.tb.BG_COLOR)

def setToolbarButtonDisabled(

self, name, disabled=True)

def setToolbarButtonDisabled(self, name, disabled=True):
    if (name not in self.widgetManager.group(WIDGET_NAMES.Toolbar)):
        raise Exception("Unknown toolbar name: " + name)
    if disabled:
        self.widgetManager.get(WIDGET_NAMES.Toolbar, name).config(state=DISABLED)
    else:
        self.widgetManager.get(WIDGET_NAMES.Toolbar, name).config(state=NORMAL)

def setToolbarButtonEnabled(

self, name)

def setToolbarButtonEnabled(self, name):
    self.setToolbarButtonDisabled(name, False)

def setToolbarDisabled(

self, disabled=True)

def setToolbarDisabled(self, disabled=True):
    for but in self.widgetManager.group(WIDGET_NAMES.Toolbar).keys():
        if disabled:
            self.widgetManager.get(WIDGET_NAMES.Toolbar, but).config(state=DISABLED)
        else:
            self.widgetManager.get(WIDGET_NAMES.Toolbar, but).config(state=NORMAL)
    if self.tb.pinBut is not None:
        if disabled:
            # this fails if not bound
            if self.tb.pinBut.eventId:
                self.tb.pinBut.unbind("<Button-1>", self.tb.pinBut.eventId)
            self.tb.pinBut.eventId = None
            self._disableTooltip(self.tb.pinBut)
            self.tb.pinBut.config(cursor="")
        else:
            if gui.GET_PLATFORM() == gui.MAC:
                self.tb.pinBut.config(cursor="pointinghand")
            elif gui.GET_PLATFORM() in [gui.WINDOWS, gui.LINUX]:
                self.tb.pinBut.config(cursor="hand2")
            self.tb.pinBut.eventId = self.tb.pinBut.bind("<Button-1>", self._toggletb)
            self._enableTooltip(self.tb.pinBut)

def setToolbarEnabled(

self)

def setToolbarEnabled(self):
    self.setToolbarDisabled(False)

def setToolbarIcon(

self, name, icon)

def setToolbarIcon(self, name, icon):
    if (name not in self.widgetManager.group(WIDGET_NAMES.Toolbar)):
        raise Exception("Unknown toolbar name: " + name)
    imgFile = os.path.join(self.icon_path, icon.lower() + ".png")
    with PauseLogger():
        self.setToolbarImage(name, imgFile)

def setToolbarImage(

self, name, imgFile)

def setToolbarImage(self, name, imgFile):
    if (name not in self.widgetManager.group(WIDGET_NAMES.Toolbar)):
        raise Exception("Unknown toolbar name: " + name)
    image = self._getImage(imgFile)
    self.widgetManager.get(WIDGET_NAMES.Toolbar, name).config(image=image)
    self.widgetManager.get(WIDGET_NAMES.Toolbar, name).image = image

def setToolbarPinned(

self, pinned=True)

def setToolbarPinned(self, pinned=True):
    self.tb.pinned = pinned
    self._setPinBut()
    if not self.tb.pinned:
        if self.tb.pinBut is not None:
            try:
                self.tb.pinBut.image = self._getImage(os.path.join(self.icon_path, "unpin.gif"))
            except:
                pass
        self.tb.makeMinBar()
        self.tb._minToolbar()
    else:
        if self.tb.pinBut is not None:
            try:
                self.tb.pinBut.image = self._getImage(os.path.join(self.icon_path, "pin.gif"))
            except:
                pass
        self.tb._maxToolbar()
    if self.tb.pinBut is not None:
        self.tb.pinBut.config(image=self.tb.pinBut.image)

def setTransparency(

self, percentage)

def setTransparency(self, percentage):
    if self.platform == self.LINUX:
        self.warn("Transparency not supported on LINUX")
    else:
        if percentage > 1:
            percentage = float(percentage) / 100
        self._getTopLevel().attributes("-alpha", percentage)

def setTreeBg(

self, title, colour)

def setTreeBg(self, title, colour):
    tree = self.widgetManager.get(WIDGET_NAMES.Tree, title)
    tree.setBgColour(colour)

def setTreeClickFunction(

self, title, func)

def setTreeClickFunction(self, title, func):
    if func is not None:
        tree = self.widgetManager.get(WIDGET_NAMES.Tree, title)
        tree.item.registerClick(title, func)

def setTreeColours(

self, title, fg=None, bg=None, fgH=None, bgH=None)

def setTreeColours(self, title, fg=None, bg=None, fgH=None, bgH=None):
    tree = self.widgetManager.get(WIDGET_NAMES.Tree, title)
    tree.setAllColours(bg, fg, bgH, fgH)

def setTreeDoubleClickFunction(

self, title, func)

def setTreeDoubleClickFunction(self, title, func):
    if func is not None:
        tree = self.widgetManager.get(WIDGET_NAMES.Tree, title)
        tree.item.registerDblClick(title, func)

def setTreeEditFunction(

self, title, func)

def setTreeEditFunction(self, title, func):
    if func is not None:
        tree = self.widgetManager.get(WIDGET_NAMES.Tree, title)
        command = self.MAKE_FUNC(func, title)
        tree.registerEditEvent(command)

def setTreeEditable(

self, title, value=True)

def setTreeEditable(self, title, value=True):
    tree = self.widgetManager.get(WIDGET_NAMES.Tree, title)
    tree.item.setCanEdit(value)

def setTreeFg(

self, title, colour)

def setTreeFg(self, title, colour):
    tree = self.widgetManager.get(WIDGET_NAMES.Tree, title)
    tree.setFgColour(colour)

def setTreeHighlightBg(

self, title, colour)

def setTreeHighlightBg(self, title, colour):
    tree = self.widgetManager.get(WIDGET_NAMES.Tree, title)
    tree.setBgHColour(colour)

def setTreeHighlightFg(

self, title, colour)

def setTreeHighlightFg(self, title, colour):
    tree = self.widgetManager.get(WIDGET_NAMES.Tree, title)
    tree.setFgHColour(colour)

def setTtkTheme(

self, theme=None)

sets the ttk theme to use

def setTtkTheme(self, theme=None):
    """ sets the ttk theme to use """
    self.ttkStyle = ttk.Style()
    gui.trace("Switching ttk theme to: %s", theme)
    if theme is not None:
        try:
            self.ttkStyle.theme_use(theme)
        except:
            gui.trace("no basic ttk theme named %s found, searching for additional themes", theme)
            self._loadTtkThemes()
            if not ThemedStyle:
                self.error("ttk theme: %s unavailable. Try one of: %s", theme, str(self.ttkStyle.theme_names()))
            else:
                self.ttkStyle.set_theme(theme)
    # set up our ttk styles
    self.ttkStyle.configure("DefaultText.TEntry", foreground="grey")
    self.ttkStyle.configure("ValidationEntryValid.TEntry", foreground="#4CC417", highlightbackground="#4CC417", highlightcolor="#4CC417", highlightthickness='20')
    self.ttkStyle.configure("ValidationEntryInvalid.TEntry", foreground="#FF0000", highlightbackground="#FF0000", highlightcolor="#FF0000", highlightthickness='20')
    self.ttkStyle.configure("ValidationEntryWait.TEntry", foreground="#000000", highlightbackground="#000000", highlightcolor="#000000", highlightthickness='20')
    self.ttkStyle.configure("ValidationEntryValid.TLabel", foreground="#4CC417")
    self.ttkStyle.configure("ValidationEntryInvalid.TLabel", foreground="#FF0000")
    self.ttkStyle.configure("ValidationEntryWait.TLabel", foreground="#000000")
    self.ttkStyle.configure("Link.TLabel", foreground="#0000ff")
    self.ttkStyle.configure("LinkOver.TLabel", foreground="#3366ff")
    #toolbars
    self.ttkStyle.configure("Toolbar.TFrame")
    self.ttkStyle.configure("Toolbar.TLabel")
    self.ttkStyle.configure("Toolbar.TButton", compound=CENTER, padding=0, expand=0)

def setValidationEntry(

self, title, state='valid')

def setValidationEntry(self, title, state="valid"):
    entry = self.widgetManager.get(WIDGET_NAMES.Entry, title)
    if not entry.isValidation:
        self.warn("Entry %s is not a validation entry. Unable to set WAITING VALID.", title)
        return
    if state == "wait":
        col = "#000000"
        text = '\u2731'
        eStyle="ValidationEntryWaiting.TEntry"
        lStyle="ValidationEntryWaiting.TLabel"
    elif state == "invalid":
        col = "#FF0000"
        text = '\u2716'
        eStyle="ValidationEntryInvalid.TEntry"
        lStyle="ValidationEntryInvalid.TLabel"
    elif state == "valid":
        col = "#4CC417"
        text = '\u2714'
        eStyle="ValidationEntryValid.TEntry"
        lStyle="ValidationEntryValid.TLabel"
    else:
        self.warn("Invalid validation state: %s", state)
        return
    if not self.ttkFlag:
        if not entry.showingDefault:
            entry.config(fg=col)
        entry.config(highlightbackground=col, highlightcolor=col)
        entry.config(highlightthickness=1)
        entry.lab.config(text=text, fg=col)
        entry.oldFg = col
    else:
        if not entry.showingDefault:
            entry.configure(style=eStyle)
        entry.lab.config(text=text, style=lStyle)
        entry.oldFg = eStyle
    entry.lab.DEFAULT_TEXT = entry.lab.cget("text")

def setValidationEntryLabelBg(

self, title, bg)

def setValidationEntryLabelBg(self, title, bg):
    ent = self.widgetManager.get(WIDGET_NAMES.Entry, title)
    if not ent.isValidation:
        raise Exception("You can only set label BGs on validation entries")
    ent.lab.config(bg=bg)

def setVisible(

self, visible=True)

def setVisible(self, visible=True):
    if visible: self.show()
    else: self.hide()

def show(

self, btn=None)

def show(self, btn=None):
    self._getTopLevel().displayed = True
    self._getTopLevel().deiconify()

def showAccess(

self, location=None)

def showAccess(self, location=None):
    self._makeAccess()
    # update current settings
    self.accessOrigFont = self.font
    self.accessOrigBg = self.bg
    self.accessOrigFg = self.fg
    self._resetAccess()
    self.showSubWindow("access_access_subwindow")

def showAllSubWindows(

self)

def showAllSubWindows(self):
    for sub in self.widgetManager.group(WIDGET_NAMES.SubWindow):
        self.showSubWindow(sub)

def showPagedWindowPageNumber(

self, title, show=True)

def showPagedWindowPageNumber(self, title, show=True):
    pager = self.widgetManager.get(WIDGET_NAMES.PagedWindow, title)
    pager.showPageNumber(show)

def showPagedWindowTitle(

self, title, show=True)

def showPagedWindowTitle(self, title, show=True):
    pager = self.widgetManager.get(WIDGET_NAMES.PagedWindow, title)
    pager.showTitle(show)

def showScaleIntervals(

self, title, intervals)

def showScaleIntervals(self, title, intervals):
    if not self.ttkFlag:
        sc = self.widgetManager.get(WIDGET_NAMES.Scale, title)
        sc.config(tickinterval=intervals)
    else:
        self.warn("ttk: showScaleIntervals() not supported: %s", title)

def showScaleValue(

self, title, show=True)

def showScaleValue(self, title, show=True):
    if not self.ttkFlag:
        sc = self.widgetManager.get(WIDGET_NAMES.Scale, title)
        sc.config(showvalue=show)
    else:
        self.warn("ttk: showScaleValue() not supported: %s", title)

def showSplash(

self, text='appJar', fill='#FF0000', stripe='#000000', fg='#FFFFFF', font=44)

creates a splash screen to show at start up

def showSplash(self, text="appJar", fill="#FF0000", stripe="#000000", fg="#FFFFFF", font=44):
    """ creates a splash screen to show at start up """
    self.splashConfig= {'text':text, 'fill':fill, 'stripe':stripe, 'fg':fg, 'font':font}

def showSubWindow(

self, title, hide=False, follow=False)

def showSubWindow(self, title, hide=False, follow=False):
    tl = self.widgetManager.get(WIDGET_NAMES.SubWindow, title)
    if hide:
        self.hideAllSubWindows()
    gui.trace("Showing subWindow %s", title)
    tl.show()
    self._bringToFront(tl)
    tl.block()
    return tl

def showTabbedFrameTab(

self, title, tab)

def showTabbedFrameTab(self, title, tab):
    nb = self.widgetManager.get(WIDGET_NAMES.TabbedFrame, title)
    nb.showTab(tab)

def showTitleBar(

self)

def showTitleBar(self):
    self.hasTitleBar = True
    self._doTitleBar()

def showToolbar(

self)

def showToolbar(self):
    self.tb.show()

def showTreeAttributes(

self, title, show=True)

def showTreeAttributes(self, title, show=True):
    tree = self.widgetManager.get(WIDGET_NAMES.Tree, title)
    self._loadTooltip()
    tree.showAttributes(show)

def showTreeMenu(

self, title, show=True)

def showTreeMenu(self, title, show=True):
    tree = self.widgetManager.get(WIDGET_NAMES.Tree, title)
    tree.showMenu(show)

def showWidgetType(

self, kind, name)

def showWidgetType(self, kind, name):
    items = self._getWidgetList(kind, name, limit=False)
    for item in items:
        if self._widgetHasContainer(kind, item):
            gui.trace("Showing widget in container: %s", name)
            widget = item.master
            if hasattr(widget, "inContainer") and widget.inContainer:
                gui.trace("Have container in container")
                widget = widget.master
            try: self.widgetManager.get(WIDGET_NAMES.FrameLabel, name).hidden = False
            except: pass
        else:
            msg = "Showing widget"
            widget = item
        # only show the widget, if it's not already showing
        if "in" not in widget.grid_info():
            gui.trace("Widget shown: %s", name)
            widget.grid()
#            self._updateLabelBoxes(name, widget.grid_info()['column'])
        else:
            gui.trace("Showing failed - %s already showing", name)

def shrinkImage(

self, name, x, y='')

def shrinkImage(self, name, x, y=''):
    label = self.widgetManager.get(WIDGET_NAMES.Image, name)
    image = label.image.subsample(x, y)
    label.config(image=image)
    label.config(anchor=CENTER, font=self._getContainerProperty('labelFont'))
    if not self.ttkFlag:
        label.config(background=self._getContainerBg())
        label.config(width=image.width(), height=image.height())
    label.modImage = image  # keep a reference!

def slider(

self, title, *args, **kwargs)

simpleGUI - alternative for scale()

def slider(self, title, *args, **kwargs):
    """ simpleGUI - alternative for scale() """
    return self.scale(title, *args, **kwargs)

def sortGrid(

self, title, columnNumber, descending=False)

def sortGrid(self, title, columnNumber, descending=False):
    return self.sortTable(title, columnNumber, descending)

def sortTable(

self, title, columnNumber, descending=False)

def sortTable(self, title, columnNumber, descending=False):
    grid = self.widgetManager.get(WIDGET_NAMES.Table, title)
    grid.sort(columnNumber, descending)

def soundError(

self)

def soundError(self):
    self._soundWrap("SystemHand")

def soundWarning(

self)

def soundWarning(self):
    self._soundWrap("SystemAsterisk")

def spin(

self, title, value=None, *args, **kwargs)

simpleGUI - shortner for spinBox()

def spin(self, title, value=None, *args, **kwargs):
    """ simpleGUI - shortner for spinBox() """
    return self.spinBox(title, value, *args, **kwargs)

def spinBox(

self, title, value=None, *args, **kwargs)

simpleGUI - adds, sets & gets spinBoxes all in one go

def spinBox(self, title, value=None, *args, **kwargs):
    """ simpleGUI - adds, sets & gets spinBoxes all in one go """
    widgKind = WIDGET_NAMES.SpinBox
    endValue = kwargs.pop("endValue", None)
    selected = kwargs.pop("selected", None)
    item = kwargs.pop("item", None)
    label = kwargs.pop("label", False)
    # select=select, deselect=<RESET>, toggle=<NONE>, clear=??, rename=set, replace=update, delete=remov
    if value is None: mode = 'get'
    else: mode = 'select'
    mode = kwargs.pop("mode", mode)
    callFunction = kwargs.pop("callFunction", True)
    try: self.widgetManager.verify(widgKind, title)
    except: # widget exists
        if mode == "select":
            if value is not None: self.setSpinBoxPos(title, value, *args, **kwargs)
            else: gui.error("No item specified to select in spinbox: %s", title)
        elif mode == "toggle":
            gui.error("%s not available on spinbox: %s", mode, title)
        elif mode in["clear", "deselect"]:
            self.clearSpinBox(title)
        elif mode == "rename":
            gui.error("%s not implemented yet in spinbox: %s", mode, title)
        elif mode == "replace":
            if value is not None: self.changeSpinBox(title, vals=value)
            else: gui.error("No values specified to replace in spinbox: %s", title)
        elif mode == "delete":
            gui.error("%s not implemented yet in spinbox: %s", mode, title)
        elif mode == "get":
            pass
        else:
            gui.error("Invalid mode (%s) specified in spinbox: %s", mode, title)
        spinBox =  self.getSpinBox(title)
    else: # new widget
        kwargs = self._parsePos(kwargs.pop("pos", []), kwargs)
        if endValue is not None:
            if label: spinBox = self.addLabelSpinBoxRange(title, value, endValue, *args, label=label, **kwargs)
            else: spinBox = self.addSpinBoxRange(title, value, endValue, *args, **kwargs)
        else:
            if label: spinBox = self.addLabelSpinBox(title, value, *args, label=label, **kwargs)
            else: spinBox = self.addSpinBox(title, value, *args, **kwargs)
    if selected is not None: self.setSpinBoxPos(title, selected)
    if item is not None: self.setSpinBox(title, item)
    if len(kwargs) > 0:
        self._configWidget(title, widgKind, **kwargs)
    return spinBox

def spinbox(

self, title, value=None, *args, **kwargs)

simpleGUI - shortner for spinBox()

def spinbox(self, title, value=None, *args, **kwargs):
    """ simpleGUI - shortner for spinBox() """
    return self.spinBox(title, value, *args, **kwargs)

def startAnimation(

self, name)

def startAnimation(self, name):
    img = self.widgetManager.get(WIDGET_NAMES.Image, name).image
    if not img.animating:
        img.animating = True
        anim_id = self.topLevel.after(img.anim_speed, self._animateImage, name)
        self.widgetManager.update(WIDGET_NAMES.AnimationID, name, anim_id)

def startContainer(

self, fType, title, row=None, column=0, colspan=0, rowspan=0, sticky=None, name=None)

def startContainer(self, fType, title, row=None, column=0, colspan=0, rowspan=0, sticky=None, name=None):
    if name is None: name = title
    if fType == WIDGET_NAMES.LabelFrame:
        # first, make a LabelFrame, and position it correctly
        self.widgetManager.verify(WIDGET_NAMES.LabelFrame, title)
        if not self.ttkFlag:
            container = LabelFrame(self.getContainer(), text=name, relief="groove")
            container.config(background=self._getContainerBg(), font=self._getContainerProperty('labelFont'))
        else:
            container = ttk.LabelFrame(self.getContainer(), text=name, relief="groove")
        container.DEFAULT_TEXT = name
        container.isContainer = True
        self.setPadX(5)
        self.setPadY(5)
        self._positionWidget(container, row, column, colspan, rowspan, "nsew")
        self.widgetManager.add(WIDGET_NAMES.LabelFrame, title, container)
        # now, add to top of stack
        self._addContainer(title, WIDGET_NAMES.LabelFrame, container, 0, 1, sticky)
        return container
    elif fType == WIDGET_NAMES.Canvas:
        # first, make a canvas, and position it correctly
        self.widgetManager.verify(WIDGET_NAMES.Canvas, title)
        container = Canvas(self.getContainer())
        container.isContainer = True
        self._positionWidget(container, row, column, colspan, rowspan, "nsew")
        self.widgetManager.add(WIDGET_NAMES.Canvas, title, container)
        # now, add to top of stack
        self._addContainer(title, WIDGET_NAMES.Canvas, container, 0, 1, "")
        return container
    elif fType == WIDGET_NAMES.TabbedFrame:
        self.widgetManager.verify(WIDGET_NAMES.TabbedFrame, title)
        tabbedFrame = self._tabbedFrameMaker(self.getContainer(), self.ttkFlag, font=self._getContainerProperty('labelFont'))
        if not self.ttkFlag:
            tabbedFrame.config(bg=self._getContainerBg())
         tabbedFrame.isContainer = True
        self._positionWidget(
            tabbedFrame,
            row,
            column,
            colspan,
            rowspan,
            sticky=sticky)
        self.widgetManager.add(WIDGET_NAMES.TabbedFrame, title, tabbedFrame)
        # now, add to top of stack
        self._addContainer(title, WIDGET_NAMES.TabbedFrame, tabbedFrame, 0, 1, sticky)
        return tabbedFrame
    elif fType == WIDGET_NAMES.Tab:
        # add to top of stack
        self.containerStack[-1]['widgets'] = True
        tabTitle = self._getContainerProperty('title') + "__" + title
        tab = self._getContainerProperty('container').addTab(title)
        self._addContainer(tabTitle, WIDGET_NAMES.Tab, tab, 0, 1, sticky)
        return tab
    elif fType == WIDGET_NAMES.Notebook:
        if not self.ttkFlag:
            raise Exception("Cannot create a ttk Notebook, unless ttk is enabled.")
        self.widgetManager.verify(WIDGET_NAMES.Notebook, title)
        notebook = ttk.Notebook(self.getContainer())
         tabbedFrame.isContainer = True
        self._positionWidget(
            notebook,
            row,
            column,
            colspan,
            rowspan,
            sticky=sticky)
        self.widgetManager.add(WIDGET_NAMES.Notebook, title, notebook)
        # now, add to top of stack
        self._addContainer(title, WIDGET_NAMES.Notebook, notebook, 0, 1, sticky)
        return notebook
    elif fType == WIDGET_NAMES.Note:
        # add to top of stack
        self.containerStack[-1]['widgets'] = True
        noteTitle = self._getContainerProperty('title') + "__" + title
        frame = ttk.Frame(self._getContainerProperty('container'))
        self._getContainerProperty('container').add(frame, text=title)
        self._addContainer(noteTitle, WIDGET_NAMES.Note, frame, 0, 1, sticky)
        return frame
    elif fType == WIDGET_NAMES.PanedFrame:
        # if we previously put a frame for widgets
        # remove it
        if self._getContainerProperty('type') == WIDGET_NAMES.Pane:
            self.stopContainer()
        # now, add the new pane
        self.widgetManager.verify(WIDGET_NAMES.PanedFrame, title)
        pane = PanedWindow(
            self.getContainer(),
            showhandle=True,
            sashrelief="groove",
            bg=self._getContainerBg())
        pane.isContainer = True
        self._positionWidget(
            pane, row, column, colspan, rowspan, sticky=sticky)
        self.widgetManager.add(WIDGET_NAMES.PanedFrame, title, pane)
        # now, add to top of stack
        self._addContainer(title, WIDGET_NAMES.PanedFrame, pane, 0, 1, sticky)
        # now, add a frame to the pane
        self.startContainer(WIDGET_NAMES.Pane, title)
        return pane
    elif fType == WIDGET_NAMES.Pane:
        # create a frame, and add it to the pane
        pane = Pane(self.getContainer(), bg=self._getContainerBg())
        pane.isContainer = True
        self._getContainerProperty('container').add(pane)
        self.widgetManager.add(WIDGET_NAMES.Pane, title, pane)
        # now, add to top of stack
        self._addContainer(title, WIDGET_NAMES.Pane, pane, 0, 1, sticky)
        return pane
    elif fType == WIDGET_NAMES.ScrollPane:
        self.widgetManager.verify(WIDGET_NAMES.ScrollPane, title)
        # naned used to diabled sctollbars
        if name not in ["horizontal", "vertical", ""]:
            gui.warn("ScrollPane %s: Invalid value for disabled, must be one of 'horizontal' or 'vertical'", title)
        scrollPane = ScrollPane(self.getContainer(), disabled=name)
        if not self.ttkFlag:
            scrollPane.config(bg=self._getContainerBg())
        scrollPane.isContainer = True
        self._positionWidget(
            scrollPane,
            row,
            column,
            colspan,
            rowspan,
            sticky=sticky)
        self.widgetManager.add(WIDGET_NAMES.ScrollPane, title, scrollPane)
        # now, add to top of stack
        self._addContainer(title, WIDGET_NAMES.ScrollPane, scrollPane, 0, 1, sticky)
        return scrollPane
    elif fType == WIDGET_NAMES.ToggleFrame:
        self.widgetManager.verify(WIDGET_NAMES.ToggleFrame, title)
        toggleFrame = ToggleFrame(self.getContainer(), title=title, bg=self._getContainerBg())
        toggleFrame.configure(font=self._getContainerProperty('labelFont'))
        toggleFrame.isContainer = True
        self._positionWidget(
            toggleFrame,
            row,
            column,
            colspan,
            rowspan,
            sticky=sticky)
        self._addContainer(title, WIDGET_NAMES.ToggleFrame, toggleFrame, 0, 1, "nw")
        self.widgetManager.add(WIDGET_NAMES.ToggleFrame, title, toggleFrame)
        return toggleFrame
    elif fType == WIDGET_NAMES.PagedWindow:
        # create the paged window
        pagedWindow = PagedWindow(self.getContainer(), title=title, bg=self._getContainerBg(), width=200, height=400, buttonFont=self._getContainerProperty('buttonFont'), titleFont=self._getContainerProperty('labelFont'))
        # bind events
        self.topLevel.bind("<Left>", pagedWindow.showPrev)
        self.topLevel.bind("<Control-Left>", pagedWindow.showFirst)
        self.topLevel.bind("<Right>", pagedWindow.showNext)
        self.topLevel.bind("<Control-Right>", pagedWindow.showLast)
        # register it as a container
        pagedWindow.isContainer = True
        self._positionWidget(pagedWindow, row, column, colspan, rowspan, sticky=sticky)
        self._addContainer(title, WIDGET_NAMES.PagedWindow, pagedWindow, 0, 1, "nw")
        self.widgetManager.add(WIDGET_NAMES.PagedWindow, title, pagedWindow)
        return pagedWindow
    elif fType == WIDGET_NAMES.Page:
        page = self._getContainerProperty('container').addPage()
        page.isContainer = True
        self._addContainer(title, WIDGET_NAMES.Page, page, 0, 1, sticky)
        self.containerStack[-1]['expand'] = "None"
        return page
    elif fType == WIDGET_NAMES.FrameStack:
        # create the paged window
        frameStack = FrameStack(self.getContainer(), bg=self._getContainerBg())
        self.widgetManager.add(WIDGET_NAMES.FrameStack, title, frameStack)
        # register it as a container
        frameStack.isContainer = True
        self._positionWidget(frameStack, row, column, colspan, rowspan, sticky=sticky)
        self._addContainer(title, WIDGET_NAMES.FrameStack, frameStack, 0, 1, "news")
        return frameStack
    elif fType == WIDGET_NAMES.Frame:
        # first, make a Frame, and position it correctly
        self.widgetManager.verify(WIDGET_NAMES.Frame, title)
        container = self._makeAjFrame()(self.getContainer())
        container.isContainer = True
        container.config(background=self._getContainerBg())
        self._positionWidget( container, row, column, colspan, rowspan, "nsew")
        self.widgetManager.add(WIDGET_NAMES.Frame, title, container)
        # now, add to top of stack
        self._addContainer(title, WIDGET_NAMES.Frame, container, 0, 1, sticky)
        return container
    elif fType == WIDGET_NAMES.SubFrame:
        subFrame = self._getContainerProperty('container').addFrame()
        subFrame.isContainer = True
        self._addContainer(title, WIDGET_NAMES.SubFrame, subFrame, 0, 1, "news")
        self.widgetManager.add(WIDGET_NAMES.Frame, title, subFrame)
        return subFrame
    else:
        raise Exception("Unknown container: " + fType)

def startFrame(

self, title=None, row=None, column=0, colspan=0, rowspan=0, sticky='NSEW')

def startFrame(self, title=None, row=None, column=0, colspan=0, rowspan=0, sticky="NSEW"):
    frameType = WIDGET_NAMES.Frame
    if self._getContainerProperty('type') == WIDGET_NAMES.FrameStack:
        # generate a frame title
        frameNum = self._getContainerProperty('container').getNumFrames()
        title = self._getContainerProperty('title') + "__" + str(frameNum)
        gui.trace("Adding new subFrame: %s", title)
        self.containerStack[-1]['widgets'] = True
        frameType = WIDGET_NAMES.SubFrame
    else:
        if title is None:
            raise Exception("All frames must have a title")
        gui.trace("Adding new frame: %s", title)
    return self.startContainer(frameType, title, row, column, colspan, rowspan, sticky)

def startFrameStack(

self, title, row=None, column=0, colspan=0, rowspan=0, sticky='news', change=None, start=-1)

def startFrameStack(self, title, row=None, column=0, colspan=0, rowspan=0, sticky="news", change=None, start=-1):
    fs = self.startContainer(WIDGET_NAMES.FrameStack, title, row, column, colspan, rowspan, sticky)
    fs.setChangeFunction(change)
    fs.setStartFrame(start)
    return fs

def startLabelFrame(

self, title, row=None, column=0, colspan=0, rowspan=0, sticky='w', hideTitle=False, label=None, name=None)

def startLabelFrame(self, title, row=None, column=0, colspan=0, rowspan=0, sticky=W, hideTitle=False, label=None, name=None):
    if label is not None: name = label
    if hideTitle: name = ''
    lf = self.startContainer(WIDGET_NAMES.LabelFrame, title, row, column, colspan, rowspan, sticky, name)
    return lf

def startNote(

self, title)

def startNote(self, title):
    # auto close the previous TAB - keep it?
    if self._getContainerProperty('type') == WIDGET_NAMES.Note:
        self.warn("You didn't STOP the previous NOTE")
        self.stopContainer()
    elif self._getContainerProperty('type') != WIDGET_NAMES.Notebook:
        raise Exception(
            "Can't add a Note to the current container: ", self._getContainerProperty('type'))
    return self.startContainer(WIDGET_NAMES.Note, title)

def startNotebook(

self, title, row=None, column=0, colspan=0, rowspan=0, sticky='NSEW')

def startNotebook(self, title, row=None, column=0, colspan=0, rowspan=0, sticky="NSEW"):
    return self.startContainer(WIDGET_NAMES.Notebook, title, row, column, colspan, rowspan, sticky)

def startPage(

self, sticky='nw')

def startPage(self, sticky="nw"):
    if self._getContainerProperty('type') == WIDGET_NAMES.Page:
        self.warn("You didn't STOP the previous PAGE")
        self.stopPage()
    elif self._getContainerProperty('type') != WIDGET_NAMES.PagedWindow:
        raise Exception("Can't start a PAGE, currently in:",
                        self._getContainerProperty('type'))
    self.containerStack[-1]['widgets'] = True
    # generate a page title
    pageNum = self._getContainerProperty('container').frameStack.getNumFrames() + 1
    pageTitle = self._getContainerProperty('title') + "__" + str(pageNum)
    return self.startContainer(WIDGET_NAMES.Page, pageTitle, row=None, column=None, colspan=None, rowspan=None, sticky=sticky)

def startPagedWindow(

self, title, row=None, column=0, colspan=0, rowspan=0)

def startPagedWindow(self, title, row=None, column=0, colspan=0, rowspan=0):
    return self.startContainer( WIDGET_NAMES.PagedWindow, title, row, column, colspan, rowspan, sticky="nsew")

def startPanedFrame(

self, title, row=None, column=0, colspan=0, rowspan=0, sticky='NSEW')

def startPanedFrame(self, title, row=None, column=0, colspan=0, rowspan=0, sticky="NSEW"):
    p = self.startContainer(WIDGET_NAMES.PanedFrame, title, row, column, colspan, rowspan, sticky)
    return p

def startPanedFrameVertical(

self, title, row=None, column=0, colspan=0, rowspan=0, sticky='NSEW')

def startPanedFrameVertical( self, title, row=None, column=0, colspan=0, rowspan=0, sticky="NSEW"):
    p = self.startPanedFrame(title, row, column, colspan, rowspan, sticky)
    self.setPanedFrameVertical(title)
    return p

def startScrollPane(

self, title, row=None, column=0, colspan=0, rowspan=0, sticky='NSEW', disabled='')

def startScrollPane(self, title, row=None, column=0, colspan=0, rowspan=0, sticky="NSEW", disabled=""):
    return self.startContainer(WIDGET_NAMES.ScrollPane, title, row, column, colspan, rowspan, sticky, disabled)

def startSubWindow(

self, name, title=None, modal=False, blocking=False, transient=False, grouped=True)

def startSubWindow(self, name, title=None, modal=False, blocking=False, transient=False, grouped=True):
    self.widgetManager.verify(WIDGET_NAMES.SubWindow, name)
    gui.trace("Starting subWindow %s", name)
    top = SubWindow(self, self.topLevel, name, title=title, stopFunc = self.confirmHideSubWindow,
                    modal=modal, blocking=blocking, transient=transient, grouped=grouped)
    ico = self._getTopLevel().winIcon
    self.widgetManager.add(WIDGET_NAMES.SubWindow, name, top)
    # now, add to top of stack
    self._addContainer(name, WIDGET_NAMES.SubWindow, top, 0, 1, "")
    # add an icon if required
    if ico is not None:
        self.setIcon(ico)
    else:
        top.winIcon = None
    return top

def startTab(

self, title, beforeTab=None, afterTab=None)

def startTab(self, title, beforeTab=None, afterTab=None):
    if beforeTab is not None and afterTab is not None:
        self.warn("You can't specify a before and after value for tab: %s", title)
        beforeTab = afterTab = None
    # auto close the previous TAB - keep it?
    if self._getContainerProperty('type') == WIDGET_NAMES.Tab:
        self.warn("You didn't STOP the previous TAB")
        self.stopContainer()
    elif self._getContainerProperty('type') != WIDGET_NAMES.TabbedFrame:
        raise Exception("Can't add a Tab to the current container: ", self._getContainerProperty('type'))
    tf = self.widgetManager.get(WIDGET_NAMES.TabbedFrame, self._getContainerProperty("title"))
    tf.setBeforeTab(beforeTab)
    tf.setAfterTab(afterTab)
    tab = self.startContainer(WIDGET_NAMES.Tab, title)
    tf.setBeforeTab()
    tf.setAfterTab()
    return tab

def startTabbedFrame(

self, title, row=None, column=0, colspan=0, rowspan=0, sticky='NSEW')

def startTabbedFrame(self, title, row=None, column=0, colspan=0, rowspan=0, sticky="NSEW"):
    return self.startContainer(WIDGET_NAMES.TabbedFrame, title, row, column, colspan, rowspan, sticky)

def startToggleFrame(

self, title, row=None, column=0, colspan=0, rowspan=0)

def startToggleFrame(self, title, row=None, column=0, colspan=0, rowspan=0):
    return self.startContainer(WIDGET_NAMES.ToggleFrame, title, row, column, colspan, rowspan, sticky="new")

def status(

self, *args, **kwargs)

def status(self, *args, **kwargs):
    self.statusbar(*args, **kwargs)

def statusbar(

self, *args, **kwargs)

simpleGUI - shortener for statusbar

def statusbar(self, *args, **kwargs):
    """ simpleGUI - shortener for statusbar """
    bg = kwargs.pop('bg', None)
    fg = kwargs.pop('fg', None)
    width = kwargs.pop('width', None)
    text = kwargs.pop('text', "")
    header = kwargs.pop('header', None)
    fields = kwargs.pop('fields', 1)
    field = kwargs.pop('field', 0)
    side = kwargs.pop('side', None)
    if not self.hasStatus:
        self.addStatusbar(header=header, fields=fields, side=side)
        self.setStatusbar(text=text)
    else:
        if len(args) > 0: text = args[0]
        if len(args) > 1: field = args[1]
        if header is not None: self.setStatusbarHeader(header)
        self.setStatusbar(text=text, field=field)
    if bg is not None: self.setStatusbarBg(bg)
    if fg is not None: self.setStatusbarFg(fg)
    if width is not None: self.setStatusbarWidth(width)

def stop(

self, event=None)

Closes the GUI. If a stop function is set, will only close the GUI if True

def stop(self, event=None):
    """ Closes the GUI. If a stop function is set, will only close the GUI if True """
    theFunc = self._getTopLevel().stopFunction
    if theFunc is None or theFunc():
        if self.useSettings:
            self.saveSettings(self.settingsFile)
        # stop the after loops
        self.alive = False
        self.topLevel.after_cancel(self.pollId)
        self.topLevel.after_cancel(self.flashId)
        if self.preloadAnimatedImageId:
            self.topLevel.after_cancel(self.preloadAnimatedImageId)
        if self.processQueueId:
            self.topLevel.after_cancel(self.processQueueId)
        # stop any animations
        for key in self.widgetManager.group(WIDGET_NAMES.AnimationID):
            self.topLevel.after_cancel(self.widgetManager.get(WIDGET_NAMES.AnimationID, key))
        # stop any maps
        for key in self.widgetManager.group(WIDGET_NAMES.Map):
            self.widgetManager.get(WIDGET_NAMES.Map, key).stopUpdates()
        # stop any sounds, ignore error when not on Windows
        try:
            self.stopSound()
        except:
            pass
        self.topLevel.quit()
        if not self.fastStop: self.topLevel.destroy()
        self.__class__.instantiated = False
        gui.info("--- GUI stopped ---")

def stopAllPanedFrames(

self)

def stopAllPanedFrames(self):
    while True:
        try:
            self.stopPanedFrame()
        except:
            break

def stopAnimation(

self, name)

def stopAnimation(self, name):
    img = self.widgetManager.get(WIDGET_NAMES.Image, name).image
    img.animating = False

def stopContainer(

self)

def stopContainer(self): self._removeContainer()

def stopFrame(

self)

def stopFrame(self):
    if self._getContainerProperty('type') not in [WIDGET_NAMES.Frame, WIDGET_NAMES.SubFrame]:
        raise Exception("Can't stop a FRAME, currently in:",
                        self._getContainerProperty('type'))
    self.stopContainer()

def stopFrameStack(

self)

def stopFrameStack(self):
    if self._getContainerProperty('type') != WIDGET_NAMES.FrameStack:
        raise Exception("Can't stop a FRAMESTACK, currently in:",
                        self._getContainerProperty('type'))
    self.stopContainer()

def stopLabelFrame(

self)

def stopLabelFrame(self):
    if self._getContainerProperty('type') != WIDGET_NAMES.LabelFrame:
        raise Exception("Can't stop a LABELFRAME, currently in:",
                        self._getContainerProperty('type'))
    self.stopContainer()

def stopNote(

self)

def stopNote(self):
    if self._getContainerProperty('type') != WIDGET_NAMES.Note:
        raise Exception("Can't stop a NOTE, currently in:",
                        self._getContainerProperty('type'))
    self.stopContainer()

def stopNotebook(

self)

def stopNotebook(self):
    # auto close the existing TAB - keep it?
    if self._getContainerProperty('type') == WIDGET_NAMES.Note:
        self.warn("You didn't STOP the previous NOTE")
        self.stopContainer()
    self.stopContainer()

def stopPage(

self)

def stopPage(self):
    if self._getContainerProperty('type') == WIDGET_NAMES.Page:
        self.stopContainer()
    else:
        raise Exception("Can't stop PAGE, currently in:",
                        self._getContainerProperty('type'))

def stopPagedWindow(

self)

def stopPagedWindow(self):
    if self._getContainerProperty('type') == WIDGET_NAMES.Page:
        self.warn("You didn't STOP the previous PAGE")
        self.stopPage()
    if self._getContainerProperty('type') != WIDGET_NAMES.PagedWindow:
        raise Exception("Can't stop a PAGEDWINDOW, currently in:",
                        self._getContainerProperty('type'))
    self._getContainerProperty('container').stopPagedWindow()
    self.stopContainer()

def stopPanedFrame(

self)

def stopPanedFrame(self):
    if self._getContainerProperty('type') == WIDGET_NAMES.Pane:
        self.stopContainer()
    if self._getContainerProperty('type') != WIDGET_NAMES.PanedFrame:
        raise Exception("Can't stop a PANEDFRAME, currently in:",
                        self._getContainerProperty('type'))
    self.stopContainer()

def stopScrollPane(

self)

def stopScrollPane(self):
    if self._getContainerProperty('type') != WIDGET_NAMES.ScrollPane:
        raise Exception("Can't stop a SCROLLPANE, currently in:",
                        self._getContainerProperty('type'))
    self.stopContainer()

def stopSound(

self)

def stopSound(self):
    self._soundWrap(None)

def stopSubWindow(

self)

def stopSubWindow(self):
    container = self.containerStack[-1]
    if container['type'] == WIDGET_NAMES.SubWindow:
        if not hasattr(container["container"], 'ms'):
            self.setMinSize(container["container"])
        self.stopContainer()
    else:
        raise Exception("Can't stop a SUBWINDOW, currently in:",
                        self._getContainerProperty('type'))

def stopTab(

self)

def stopTab(self):
    if self._getContainerProperty('type') != WIDGET_NAMES.Tab:
        raise Exception("Can't stop a TAB, currently in:",
                        self._getContainerProperty('type'))
    self.stopContainer()

def stopTabbedFrame(

self)

def stopTabbedFrame(self):
    # auto close the existing TAB - keep it?
    if self._getContainerProperty('type') == WIDGET_NAMES.Tab:
        self.warn("You didn't STOP the previous TAB")
        self.stopContainer()
    self.stopContainer()

def stopToggleFrame(

self)

def stopToggleFrame(self):
    if self._getContainerProperty('type') != WIDGET_NAMES.ToggleFrame:
        raise Exception("Can't stop a TOGGLEFRAME, currently in:",
                        self._getContainerProperty('type'))
    self._getContainerProperty('container').stop()
    self.stopContainer()

def stringBox(

self, title, message, parent=None)

def stringBox(self, title, message, parent=None):
    self.topLevel.update_idletasks()
    if parent is None:
        return SimpleDialog.askstring(title, message)
    else:
        parent = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent)
        opts = {"parent": parent}
        return SimpleDialog.askstring(title=title, message=message, **opts)

def subWindow(

*args, **kwds)

@contextmanager
def subWindow(self, name, title=None, modal=False, blocking=False, transient=False, grouped=True, **kwargs):
    visible = kwargs.pop("visible", None)
    try:
        sw = self.startSubWindow(name, title, modal, blocking, transient, grouped)
    except ItemLookupError:
        sw = self.openSubWindow(name)
    self.configure(**kwargs)
    try:
        yield sw
    finally:
        self.stopSubWindow()
    if visible is True: self.showSubWindow(name)

def tab(

*args, **kwds)

@contextmanager
def tab(self, title, tabTitle=None, **kwargs):
    beforeTab = kwargs.pop("beforeTab", None)
    afterTab = kwargs.pop("afterTab", None)
    if tabTitle is None:
        try:
            tab = self.startTab(title, beforeTab, afterTab)
        except ItemLookupError:
            if self._getContainerProperty('type') != WIDGET_NAMES.TabbedFrame:
                raise Exception("Can't open a Tab in the current container: ", self._getContainerProperty('type'))
            else:
                tabTitle = self._getContainerProperty('title')
                tab = self.openTab(tabTitle, title)
    else:
        tab = self.openTab(title, tabTitle)
    self.configure(**kwargs)
    try: yield tab
    finally: self.stopTab()

def tabbedFrame(

*args, **kwds)

@contextmanager
def tabbedFrame(self, title, row=None, column=0, colspan=0, rowspan=0, sticky="NSEW", **kwargs):
    try:
        tabs = self.startTabbedFrame(title, row, column, colspan, rowspan, sticky)
    except ItemLookupError:
        tabs = self.openTabbedFrame(title)
    command = kwargs.pop("change", None)
    if command is not None: self.setTabbedFrameChangeCommand(title, command)
    self.configure(**kwargs)
    try: yield tabs
    finally: self.stopTabbedFrame()

def table(

self, title, value=None, *args, **kwargs)

simpleGUI - adds, sets & gets tables all in one go

def table(self, title, value=None, *args, **kwargs):
    """ simpleGUI - adds, sets & gets tables all in one go """
    widgKind = WIDGET_NAMES.Table
    kind = kwargs.pop("kind", 'normal')
    action=kwargs.pop('action', None)
    addRow=kwargs.pop('addRow', None)
    actionHeading=kwargs.pop('actionHeading', "Action")
    actionButton=kwargs.pop('actionButton', "Press")
    addButton=kwargs.pop('addButton', "Add")
    showMenu=kwargs.pop('showMenu', False)
    horiz=kwargs.pop('horizontal', True)
    change=kwargs.pop('change', None)
    edit=kwargs.pop('edit', None)
    try: self.widgetManager.verify(widgKind, title)
    except: # widget exists
        if value is not None: self.replaceAllTableRows(title, value)
        table = self.getTableEntries(title)
    else: # new widget
        kwargs = self._parsePos(kwargs.pop("pos", []), kwargs)
        if kind == 'normal':
            table = self.addTable(title, value, *args,
                        action=action, addRow=addRow, actionHeading=actionHeading, actionButton=actionButton,
                        addButton=addButton, showMenu=showMenu, horizontal=horiz, **kwargs
                    )
        else:
            table = self.addDbTable(title, value, *args,
                        action=action, addRow=addRow, actionHeading=actionHeading, actionButton=actionButton,
                        addButton=addButton, showMenu=showMenu, horizontal=horiz, **kwargs
                    )
    if change is not None: self.setTableChangeFunction(title, change)
    if edit is not None: self.setTableEditFunction(title, edit)
    if len(kwargs) > 0:
        self._configWidget(title, widgKind, **kwargs)
    return table

def text(

self, title, value=None, *args, **kwargs)

simpleGUI - shortner for textArea()

def text(self, title, value=None, *args, **kwargs):
    """ simpleGUI - shortner for textArea() """
    return self.textArea(title, value, *args, **kwargs)

def textArea(

self, title, value=None, *args, **kwargs)

adds, sets & gets textAreas all in one go

def textArea(self, title, value=None, *args, **kwargs):
    """ adds, sets & gets textAreas all in one go """
    widgKind = WIDGET_NAMES.TextArea
    scroll = kwargs.pop("scroll", False)
    end = kwargs.pop("end", True)
    replace = kwargs.pop("replace", False)
    callFunction = kwargs.pop("callFunction", True)
    disabled = kwargs.pop("disabled", False)
    tag = kwargs.pop("tag", None)
    tags = kwargs.pop("tags", [])
    try: self.widgetManager.verify(WIDGET_NAMES.TextArea, title)
    except: # widget exists
        text = self.getTextArea(title)
    else: # new widget
        kwargs = self._parsePos(kwargs.pop("pos", []), kwargs)
        if scroll: text = self._textMaker(title, "scroll", *args, **kwargs)
        else: text = self._textMaker(title, "text", *args, **kwargs)
        callFunction = False
    # create any tags
    for _tag in tags:
        self.textAreaCreateTag(title, _tag[0], **_tag[1])
    if replace: self.clearTextArea(title)
    if value is not None: self.setTextArea(title, value, end=end, callFunction=callFunction, tag=tag)
    if disabled: self.disableTextArea(title)
    if len(kwargs) > 0:
        self._configWidget(title, widgKind, **kwargs)
    return text

def textAreaApplyFontRange(

self, title, tag, start, end='end')

removes the tag from the specified range

def textAreaApplyFontRange(self, title, tag, start, end=END):
    """removes the tag from the specified range """
    ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title)
    tag = ta.verifyFontTag(tag)
    if tag != "UNDERLINE":
        ta.tag_remove("AJ_BOLD", start, end)
        ta.tag_remove("AJ_ITALIC", start, end)
        ta.tag_remove("AJ_BOLD_ITALIC", start, end)
    ta.tag_add("AJ_" + tag, start, end)

def textAreaApplyFontSelected(

self, title, tag)

def textAreaApplyFontSelected(self, title, tag):
    if self.widgetManager.get(WIDGET_NAMES.TextArea, title).tag_ranges(SEL):
        self.textAreaApplyFontRange(title, tag, SEL_FIRST, SEL_LAST)
    self.widgetManager.get(WIDGET_NAMES.TextArea, title).focus_set()

def textAreaChangeTag(

self, title, name, **kwargs)

changes a tag on the specified text area

def textAreaChangeTag(self, title, name, **kwargs):
    """ changes a tag on the specified text area """
    ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title)
    ta.tag_config(name, **kwargs)

def textAreaChanged(

self, title)

Creates a temporary md5 hash - and compares it with a previously generated & stored hash The previous hash has to be generated manually, by calling logTextArea

:param title: the TextArea to hash :returns: bool - True if the TextArea has changed or False if it hasn't :raises ItemLookupError: if the title can't be found

def textAreaChanged(self, title):
    """ Creates a temporary md5 hash - and compares it with a previously generated & stored hash
    The previous hash has to be generated manually, by calling logTextArea
    :param title: the TextArea to hash
    :returns: bool - True if the TextArea has changed or False if it hasn't
    :raises ItemLookupError: if the title can't be found
    """
    self._loadHashlib()
    if hashlib is False:
        self.warn("Unable to log TextArea, hashlib library not available")
    else:
        text = self.widgetManager.get(WIDGET_NAMES.TextArea, title)
        return text.__hash != text.getTextAreaHash()

def textAreaCreateTag(

self, title, name, **kwargs)

creates a new tag on the specified text area

def textAreaCreateTag(self, title, name, **kwargs):
    """ creates a new tag on the specified text area """
    ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title)
    ta.tag_config(name, **kwargs)

def textAreaDeleteTag(

self, title, *tags)

deletes the specified tag

def textAreaDeleteTag(self, title, *tags):
    """ deletes the specified tag """
    ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title)
    ta.tag_delete(*tags)

def textAreaTagPattern(

self, title, tag, pattern, regexp=False)

applies the tag to the specified text

def textAreaTagPattern(self, title, tag, pattern, regexp=False):
    """ applies the tag to the specified text """
    ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title)
    ta.highlightPattern(pattern, tag, regexp=regexp)

def textAreaTagRange(

self, title, tag, start, end='end')

applies the tag to the specified range

def textAreaTagRange(self, title, tag, start, end=END):
    """ applies the tag to the specified range """
    ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title)
    ta.tag_add(tag, start, end)

def textAreaTagSelected(

self, title, tag)

def textAreaTagSelected(self, title, tag):
    if self.widgetManager.get(WIDGET_NAMES.TextArea, title).tag_ranges(SEL):
        self.textAreaTagRange(title, tag, SEL_FIRST, SEL_LAST)
    self.widgetManager.get(WIDGET_NAMES.TextArea, title).focus_set()

def textAreaToggleFontRange(

self, title, tag, start, end='end')

will toggle the tag at the specified range

def textAreaToggleFontRange(self, title, tag, start, end=END):
    """ will toggle the tag at the specified range """
    ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title)
    tag = ta.verifyFontTag(tag)
    if tag in ta.tag_names(start):
        ta.tag_remove("AJ_"+tag, start, end)
    else:
        self.textAreaApplyFontRange(title, tag, start, end)

def textAreaToggleFontSelected(

self, title, tag)

def textAreaToggleFontSelected(self, title, tag):
    if self.widgetManager.get(WIDGET_NAMES.TextArea, title).tag_ranges(SEL):
        self.textAreaToggleFontRange(title, tag, SEL_FIRST, SEL_LAST)
    self.widgetManager.get(WIDGET_NAMES.TextArea, title).focus_set()

def textAreaToggleTagRange(

self, title, tag, start, end='end')

will toggle the tag at the specified range

def textAreaToggleTagRange(self, title, tag, start, end=END):
    """ will toggle the tag at the specified range """
    ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title)
    if tag in ta.tag_names(start): self.textAreaUntagRange(title, tag, start, end)
    else: self.textAreaTagRange(title, tag, start, end)

def textAreaToggleTagSelected(

self, title, tag)

def textAreaToggleTagSelected(self, title, tag):
    if self.widgetManager.get(WIDGET_NAMES.TextArea, title).tag_ranges(SEL):
        self.textAreaToggleTagRange(title, tag, SEL_FIRST, SEL_LAST)
    self.widgetManager.get(WIDGET_NAMES.TextArea, title).focus_set()

def textAreaUntagRange(

self, title, tag, start, end='end')

removes the tag from the specified range

def textAreaUntagRange(self, title, tag, start, end=END):
    """removes the tag from the specified range """
    ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title)
    ta.tag_remove(tag, start, end)

def textAreaUntagSelected(

self, title, tag)

def textAreaUntagSelected(self, title, tag):
    if self.widgetManager.get(WIDGET_NAMES.TextArea, title).tag_ranges(SEL):
        self.textAreaUntagRange(title, tag, SEL_FIRST, SEL_LAST)
    self.widgetManager.get(WIDGET_NAMES.TextArea, title).focus_set()

def textBox(

self, title='Text Box', question='Enter text', defaultValue=None, parent=None)

def textBox(self, title="Text Box", question="Enter text", defaultValue=None, parent=None):
    self.topLevel.update_idletasks()
    if defaultValue is not None:
        defaultVar = StringVar(self.topLevel)
        defaultVar.set(defaultValue)
    else:
        defaultVar = None
    if parent is None:
        parent = self.topLevel
    else:
        parent = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent)
    return TextDialog(parent, title, question, defaultVar=defaultVar).result

def thread(

self, func, *args, **kwargs)

will run the supplied function in a separate thread

param func: the function to run

def thread(self, func, *args, **kwargs):
    """ will run the supplied function in a separate thread
    param func: the function to run
    """
    self._loadThreading()
    if Queue is False:
        gui.warn("Unable to queueFunction - threading not possible.")
    else:
        t = Thread(group=None, target=func, name=None, args=args, kwargs=kwargs)
        t.daemon = True
        t.start()

def threadCallback(

self, func, callback, *args, **kwargs)

Run a given method in a new thread with passed arguments. When func completes call the callback with the result.

:param func: Method that returns the result. :param callback: Method that receives the result. :param args: Positional arguments for func. :param kwargs: Keyword args for func.

def threadCallback(self, func, callback, *args, **kwargs):
    """Run a given method in a new thread with passed arguments.
       When func completes call the callback with the result.
       :param func: Method that returns the result.
       :param callback: Method that receives the result.
       :param args: Positional arguments for func.
       :param kwargs: Keyword args for func.
    """
    def innerThread(func, callback, *args, **kwargs):
        result = func(*args, **kwargs)
        self.queueFunction(callback, result)
    if not callable(func) or not callable(callback):
        gui.error("Function (or callback) method isn't callable!")
        return
    self.thread(innerThread, func, callback, *args, **kwargs)

def tick(

self, title, value=None, *args, **kwargs)

simpleGUI - shortner for checkBox()

def tick(self, title, value=None, *args, **kwargs):
    """ simpleGUI - shortner for checkBox() """
    return self.checkBox(title, value, *args, **kwargs)

def toggleFrame(

*args, **kwds)

@contextmanager
def toggleFrame(self, title, row=None, column=0, colspan=0, rowspan=0, **kwargs):
    try:
        tog = self.startToggleFrame(title, row, column, colspan, rowspan)
    except ItemLookupError:
        tog = self.openToggleFrame(title)
    self.configure(**kwargs)
    try: yield tog
    finally: self.stopToggleFrame()

def toggleToggleFrame(

self, title)

def toggleToggleFrame(self, title):
    toggle = self.widgetManager.get(WIDGET_NAMES.ToggleFrame, title)
    toggle.toggle()

def toolbar(

self, names, funcs, **kwargs)

simpleGUI - shortener for toolbar

def toolbar(self, names, funcs, **kwargs):
    """ simpleGUI - shortener for toolbar """
    icons = kwargs.pop('icons', kwargs.pop('findIcon', False))
    pinned = kwargs.pop('pinned', None)
    disabled = kwargs.pop('disabled', None)
    hidden = kwargs.pop('hidden', None)
    status = kwargs.pop('status', None)
    bg = kwargs.pop('bg', None)
    if bg is not None:
        self.setToolbarBg(bg)
    self.addToolbar(names, funcs, findIcon=icons is not False)
    # allow status and icon name to be passed in a list
    for x, n in enumerate(names):
        if icons is not None:
            try: self.setToolbarIcon(n, icons[x])
            except: pass
        if status is not None:
            try: self.setToolbarButtonDisabled(n, not status[x])
            except: pass
    if pinned is not None: self.setToolbarPinned(pinned=pinned)
    if disabled is not None: self.setToolbarDisabled(disabled=disabled)
    if hidden is True: self.hideToolbar()

def trace(

message, *args)

wrapper for logMessage - setting level to TRACE

@staticmethod
def trace(message, *args):
    """ wrapper for logMessage - setting level to TRACE """
    gui.logMessage(message, "TRACE", *args)

def translate(

self, key, default=None)

returns a translated version of the key, using the current language if none found, returns the default value

def translate(self, key, default=None):
    """ returns a translated version of the key, using the current language
        if none found, returns the default value """
    return self._translate(key, "EXTERNAL", default)

def tree(

self, title, value=None, *args, **kwargs)

simpleGUI - adds, sets & gets trees all in one go

def tree(self, title, value=None, *args, **kwargs):
    """ simpleGUI - adds, sets & gets trees all in one go """
    widgKind = WIDGET_NAMES.Tree
    click = kwargs.pop("click", None)
    dblClick = kwargs.pop("dbl", None)
    edit = kwargs.pop("edit", None)
    editable = kwargs.pop("editable", None)
    showAttr = kwargs.pop("attributes", None)
    showMenu = kwargs.pop("menu", None)
    fg = kwargs.pop("fg", None)
    bg = kwargs.pop("bg", None)
    fgH = kwargs.pop("fgH", None)
    bgH = kwargs.pop("bgH", None)
    try: self.widgetManager.verify(widgKind, title)
    except: # widget exists
        tree = self.getTree(title)
    else: # new widget
        kwargs = self._parsePos(kwargs.pop("pos", []), kwargs)
        tree = self.addTree(title, value, *args, **kwargs)
    if len(kwargs) > 0:
        self._configWidget(title, widgKind, **kwargs)
    self.setTreeColours(title, fg, bg, fgH, bgH)
    if click is not None: self.setTreeClickFunction(title, click)
    if edit is not None: self.setTreeEditFunction(title, edit)
    if dblClick is not None: self.setTreeDoubleClickFunction(title, dblClick)
    if editable is not None: self.setTreeEditable(title, editable)
    if showAttr is not None: self.showTreeAttributes(title, showAttr)
    if showMenu is not None: self.showTreeMenu(title, showMenu)
    return tree

def unbindKey(

self, key)

unbinds the specified key from whatever functions it is bound to

def unbindKey(self, key):
    """ unbinds the specified key from whatever functions it is bound to """
    if key[0] == "<":
        gui.warn("Shortcuts should not include chevrons: %s", key)
        key= key[1:-1]
    self.widgetManager.get(WIDGET_NAMES.Bindings, key).removeBindings()
    self.widgetManager.remove(WIDGET_NAMES.Bindings, key)

def unbindKeys(

self, keys)

unbinds the specified keys from whatever functions they are bound to

def unbindKeys(self, keys):
    """ unbinds the specified keys from whatever functions they are bound to """
    for key in keys:
        self.unbindKey(key)

def updateListBox(

self, title, items, select=False, callFunction=True)

def updateListBox(self, title, items, select=False, callFunction=True):
    self.clearListBox(title, callFunction=callFunction)
    self.addListItems(title, items, select=select)

def updatePlot(

self, title, t, s, keepLabels=False)

def updatePlot(self, title, t, s, keepLabels=False):
    axes = self.widgetManager.get(WIDGET_NAMES.Plot, title).axes
    if keepLabels:
        xLab = axes.get_xlabel()
        yLab = axes.get_ylabel()
        pTitle = axes.get_title()
        handles, legends = axes.get_legend_handles_labels()
    axes.clear()
    axes.plot(t, s)
    if keepLabels:
        axes.set_xlabel(xLab)
        axes.set_ylabel(yLab)
        axes.set_title(pTitle)
        axes.legend(handles, legends)
    self.refreshPlot(title)
    return axes

def warn(

message, *args)

wrapper for logMessage - setting level to WARNING

@staticmethod
def warn(message, *args):
    """ wrapper for logMessage - setting level to WARNING """
    gui.logMessage(message, "WARNING", *args)

def warningBox(

self, title, message, parent=None)

def warningBox(self, title, message, parent=None):
    self.topLevel.update_idletasks()
    if parent is None:
        MessageBox.showwarning(title, message)
        if self.topLevel.displayed:
            self._bringToFront()
    else:
        parent = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent)
        opts = {"parent": parent}
        MessageBox.showwarning(title, message, **opts)
        self._bringToFront(parent)

def yesNoBox(

self, title, message, parent=None)

def yesNoBox(self, title, message, parent=None):
    self.topLevel.update_idletasks()
    if parent is None:
        return MessageBox.askyesno(title, message)
    else:
        parent = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent)
        opts = {"parent": parent}
        return MessageBox.askyesno(title=title, message=message, **opts)

def zoomGoogleMap(

self, title, mod)

def zoomGoogleMap(self, title, mod):
    gMap = self.widgetManager.get(WIDGET_NAMES.Map, title)
    if mod in ["+", "-"]:
        gMap.zoom(mod)
    elif isinstance(mod, int) and 0 <= mod <= 22:
        gMap.setZoom(mod)

def zoomImage(

self, name, x, y='')

def zoomImage(self, name, x, y=''):
    if x <= 0:
        self.shrinkImage(name, x * -1, y * -1)
    else:
        self.growImage(name, x, y)

Instance variables

var EVENT_SIZE

var EVENT_SPEED

var Widgets

var accessMade

var alive

var appJarIcon

var appWindow

var bg

var built

var buttonFont

var colspan

var configParser

var containerStack

var copyAndPaste

var dnd

var doFlash

var editMenu

var enterKey

var events

var expand

var externalSettings

var fastStop

var fg

var font

var fonts

var fullscreen

var guiPadding

var hasMenu

var hasStatus

var hasTitleBar

var icon

var icon_path

var inPadding

var inputFont

var labelFont

var language

returns the current language

var location

var logFile

var logLevel

var padding

var platform

var pollTime

var preloadAnimatedImageId

var processQueueId

var randomColour

generates a random colour

var resizable

var resource_path

var row

var rowspan

var settingsFile

var size

var sound_path

var splashConfig

var startFunction

var startWindow

var statusFont

var sticky

var stopFunction

var stretch

var tableFont

var tb

var title

var top

var topLevel

var translations

var transparency

var ttkFlag

var ttkTheme

var useSettings

var userImages

var userSounds

var validateNumeric

var validateSpinBox

var visible

var widgetManager